From 6198221fa0df0298513b35796f63f242ea97134e Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Wed, 29 May 2013 15:59:39 -0600 Subject: NVMe: Disk IO statistics Add io stats accounting for bio requests so nvme block devices show useful disk stats. Signed-off-by: Keith Busch Signed-off-by: Matthew Wilcox --- include/linux/nvme.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/nvme.h b/include/linux/nvme.h index f451c8d6e231..5d7c07946fbe 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -572,6 +572,7 @@ struct nvme_iod { int offset; /* Of PRP list */ int nents; /* Used in scatterlist */ int length; /* Of data, in bytes */ + unsigned long start_time; dma_addr_t first_dma; struct scatterlist sg[0]; }; -- cgit v1.2.3-71-gd317 From faef31b482549640e2d0095afdf3dedb992cfa80 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 21 Feb 2013 15:04:08 -0800 Subject: clocksource: time-armada-370-xp: Fix sparse warning drivers/clocksource/time-armada-370-xp.c:217:13: warning: symbol 'armada_370_xp_timer_init' was not declared. Should it be static? Also remove the __init marking in the prototype as it's unnecessary and drop the init.h file. Acked-by: Gregory CLEMENT Acked-by: Marc Zyngier Signed-off-by: Stephen Boyd --- drivers/clocksource/time-armada-370-xp.c | 3 ++- include/linux/time-armada-370-xp.h | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c index efdca3263afe..b1e1d92a8839 100644 --- a/drivers/clocksource/time-armada-370-xp.c +++ b/drivers/clocksource/time-armada-370-xp.c @@ -28,9 +28,10 @@ #include #include #include +#include +#include #include -#include /* * Timer block registers. */ diff --git a/include/linux/time-armada-370-xp.h b/include/linux/time-armada-370-xp.h index dfdfdc03115b..6fb0856b9405 100644 --- a/include/linux/time-armada-370-xp.h +++ b/include/linux/time-armada-370-xp.h @@ -11,8 +11,6 @@ #ifndef __TIME_ARMADA_370_XPPRCMU_H #define __TIME_ARMADA_370_XPPRCMU_H -#include - -void __init armada_370_xp_timer_init(void); +void armada_370_xp_timer_init(void); #endif -- cgit v1.2.3-71-gd317 From f825c736e75b11adb59ec52a4a1096efddd2ec97 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 2 Jul 2013 11:15:15 +0530 Subject: mm/cma: Move dma contiguous changes into a seperate config We want to use CMA for allocating hash page table and real mode area for PPC64. Hence move DMA contiguous related changes into a seperate config so that ppc64 can enable CMA without requiring DMA contiguous. Acked-by: Michal Nazarewicz Acked-by: Paul Mackerras Signed-off-by: Aneesh Kumar K.V [removed defconfig changes] Signed-off-by: Marek Szyprowski --- arch/arm/include/asm/dma-contiguous.h | 2 +- arch/arm/mm/dma-mapping.c | 6 +++--- drivers/base/Kconfig | 20 ++++---------------- drivers/base/Makefile | 2 +- include/linux/dma-contiguous.h | 2 +- mm/Kconfig | 24 ++++++++++++++++++++++++ 6 files changed, 34 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/arch/arm/include/asm/dma-contiguous.h b/arch/arm/include/asm/dma-contiguous.h index 3ed37b4d93da..e072bb2ba1b1 100644 --- a/arch/arm/include/asm/dma-contiguous.h +++ b/arch/arm/include/asm/dma-contiguous.h @@ -2,7 +2,7 @@ #define ASMARM_DMA_CONTIGUOUS_H #ifdef __KERNEL__ -#ifdef CONFIG_CMA +#ifdef CONFIG_DMA_CMA #include #include diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index ef3e0f3aac96..1fb40dc37ec2 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -358,7 +358,7 @@ static int __init atomic_pool_init(void) if (!pages) goto no_pages; - if (IS_ENABLED(CONFIG_CMA)) + if (IS_ENABLED(CONFIG_DMA_CMA)) ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page, atomic_pool_init); else @@ -670,7 +670,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, addr = __alloc_simple_buffer(dev, size, gfp, &page); else if (!(gfp & __GFP_WAIT)) addr = __alloc_from_pool(size, &page); - else if (!IS_ENABLED(CONFIG_CMA)) + else if (!IS_ENABLED(CONFIG_DMA_CMA)) addr = __alloc_remap_buffer(dev, size, gfp, prot, &page, caller); else addr = __alloc_from_contiguous(dev, size, prot, &page, caller); @@ -759,7 +759,7 @@ static void __arm_dma_free(struct device *dev, size_t size, void *cpu_addr, __dma_free_buffer(page, size); } else if (__free_from_pool(cpu_addr, size)) { return; - } else if (!IS_ENABLED(CONFIG_CMA)) { + } else if (!IS_ENABLED(CONFIG_DMA_CMA)) { __dma_free_remap(cpu_addr, size); __dma_free_buffer(page, size); } else { diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 07abd9d76f7f..10cd80af2aec 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -202,11 +202,9 @@ config DMA_SHARED_BUFFER APIs extension; the file's descriptor can then be passed on to other driver. -config CMA - bool "Contiguous Memory Allocator" - depends on HAVE_DMA_CONTIGUOUS && HAVE_MEMBLOCK - select MIGRATION - select MEMORY_ISOLATION +config DMA_CMA + bool "DMA Contiguous Memory Allocator" + depends on HAVE_DMA_CONTIGUOUS && CMA help This enables the Contiguous Memory Allocator which allows drivers to allocate big physically-contiguous blocks of memory for use with @@ -215,17 +213,7 @@ config CMA For more information see . If unsure, say "n". -if CMA - -config CMA_DEBUG - bool "CMA debug messages (DEVELOPMENT)" - depends on DEBUG_KERNEL - help - Turns on debug messages in CMA. This produces KERN_DEBUG - messages for every CMA call as well as various messages while - processing calls such as dma_alloc_from_contiguous(). - This option does not affect warning and error messages. - +if DMA_CMA comment "Default contiguous memory area size:" config CMA_SIZE_MBYTES diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 4e22ce3ed73d..5d93bb519753 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -6,7 +6,7 @@ obj-y := core.o bus.o dd.o syscore.o \ attribute_container.o transport_class.o \ topology.o obj-$(CONFIG_DEVTMPFS) += devtmpfs.o -obj-$(CONFIG_CMA) += dma-contiguous.o +obj-$(CONFIG_DMA_CMA) += dma-contiguous.o obj-y += power/ obj-$(CONFIG_HAS_DMA) += dma-mapping.o obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h index 01b5c84be828..00141d3325fe 100644 --- a/include/linux/dma-contiguous.h +++ b/include/linux/dma-contiguous.h @@ -57,7 +57,7 @@ struct cma; struct page; struct device; -#ifdef CONFIG_CMA +#ifdef CONFIG_DMA_CMA /* * There is always at least global CMA area and a few optional device diff --git a/mm/Kconfig b/mm/Kconfig index e742d06285b7..26a5f815cfc3 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -477,3 +477,27 @@ config FRONTSWAP and swap data is stored as normal on the matching swap device. If unsure, say Y to enable frontswap. + +config CMA + bool "Contiguous Memory Allocator" + depends on HAVE_MEMBLOCK + select MIGRATION + select MEMORY_ISOLATION + help + This enables the Contiguous Memory Allocator which allows other + subsystems to allocate big physically-contiguous blocks of memory. + CMA reserves a region of memory and allows only movable pages to + be allocated from it. This way, the kernel can use the memory for + pagecache and when a subsystem requests for contiguous area, the + allocated pages are migrated away to serve the contiguous request. + + If unsure, say "n". + +config CMA_DEBUG + bool "CMA debug messages (DEVELOPMENT)" + depends on DEBUG_KERNEL && CMA + help + Turns on debug messages in CMA. This produces KERN_DEBUG + messages for every CMA call as well as various messages while + processing calls such as dma_alloc_from_contiguous(). + This option does not affect warning and error messages. -- cgit v1.2.3-71-gd317 From b3a070cccb9135f8bec63d9f194ddaa422136fb0 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 5 Jul 2013 14:41:02 -0700 Subject: drm: pre allocate node for create_block For an upcoming patch where we introduce the i915 VMA, it's ideal to have the drm_mm_node as part of the VMA struct (ie. it's pre-allocated). Part of the conversion to VMAs is to kill off obj->gtt_space. Doing this will break a bunch of code, but amongst them are 2 callers of drm_mm_create_block(), both related to stolen memory. It also allows us to embed the drm_mm_node into the object currently which provides a nice transition over to the new code. v2: Reordered to do before ripping out obj->gtt_offset. Some minor cleanups made available because of reordering. v3: s/continue/break on failed stolen node allocation (David) Set obj->gtt_space on failed node allocation (David) Only unref stolen (fix double free) on failed create_stolen (David) Free node, and NULL it in failed create_stolen (David) Add back accidentally removed newline (David) CC: Reviewed-by: David Herrmann Signed-off-by: Ben Widawsky Acked-by: David Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_mm.c | 16 +++++---------- drivers/gpu/drm/i915/i915_gem_gtt.c | 20 ++++++++++++++---- drivers/gpu/drm/i915/i915_gem_stolen.c | 37 +++++++++++++++++++++++++--------- include/drm/drm_mm.h | 9 +++++---- 4 files changed, 53 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 07cf99cc8862..9e8dfbc1955e 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -147,12 +147,10 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, } } -struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm, - unsigned long start, - unsigned long size, - bool atomic) +int drm_mm_create_block(struct drm_mm *mm, struct drm_mm_node *node, + unsigned long start, unsigned long size) { - struct drm_mm_node *hole, *node; + struct drm_mm_node *hole; unsigned long end = start + size; unsigned long hole_start; unsigned long hole_end; @@ -161,10 +159,6 @@ struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm, if (hole_start > start || hole_end < end) continue; - node = drm_mm_kmalloc(mm, atomic); - if (unlikely(node == NULL)) - return NULL; - node->start = start; node->size = size; node->mm = mm; @@ -184,11 +178,11 @@ struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm, node->hole_follows = 1; } - return node; + return 0; } WARN(1, "no hole found for block 0x%lx + 0x%lx\n", start, size); - return NULL; + return -ENOSPC; } EXPORT_SYMBOL(drm_mm_create_block); diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 66929eac6367..88180a597c0a 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -629,14 +629,26 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, /* Mark any preallocated objects as occupied */ list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { + int ret; DRM_DEBUG_KMS("reserving preallocated space: %x + %zx\n", obj->gtt_offset, obj->base.size); BUG_ON(obj->gtt_space != I915_GTT_RESERVED); - obj->gtt_space = drm_mm_create_block(&dev_priv->mm.gtt_space, - obj->gtt_offset, - obj->base.size, - false); + obj->gtt_space = kzalloc(sizeof(*obj->gtt_space), GFP_KERNEL); + if (!obj->gtt_space) { + DRM_ERROR("Failed to preserve object at offset %x\n", + obj->gtt_offset); + continue; + } + ret = drm_mm_create_block(&dev_priv->mm.gtt_space, + obj->gtt_space, + obj->gtt_offset, + obj->base.size); + if (ret) { + DRM_DEBUG_KMS("Reservation failed\n"); + kfree(obj->gtt_space); + obj->gtt_space = NULL; + } obj->has_global_gtt_mapping = 1; } diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 0f18d75fa9f8..2746ff2d846a 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -330,6 +330,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; struct drm_mm_node *stolen; + int ret; if (dev_priv->mm.stolen_base == 0) return NULL; @@ -344,11 +345,15 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, if (WARN_ON(size == 0)) return NULL; - stolen = drm_mm_create_block(&dev_priv->mm.stolen, - stolen_offset, size, - false); - if (stolen == NULL) { + stolen = kzalloc(sizeof(*stolen), GFP_KERNEL); + if (!stolen) + return NULL; + + ret = drm_mm_create_block(&dev_priv->mm.stolen, stolen, stolen_offset, + size); + if (ret) { DRM_DEBUG_KMS("failed to allocate stolen space\n"); + kfree(stolen); return NULL; } @@ -369,13 +374,18 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, * later. */ if (drm_mm_initialized(&dev_priv->mm.gtt_space)) { - obj->gtt_space = drm_mm_create_block(&dev_priv->mm.gtt_space, - gtt_offset, size, - false); - if (obj->gtt_space == NULL) { + obj->gtt_space = kzalloc(sizeof(*obj->gtt_space), GFP_KERNEL); + if (!obj->gtt_space) { + DRM_DEBUG_KMS("-ENOMEM stolen GTT space\n"); + goto unref_out; + } + + ret = drm_mm_create_block(&dev_priv->mm.gtt_space, + obj->gtt_space, + gtt_offset, size); + if (ret) { DRM_DEBUG_KMS("failed to allocate stolen GTT space\n"); - drm_gem_object_unreference(&obj->base); - return NULL; + goto free_out; } } else obj->gtt_space = I915_GTT_RESERVED; @@ -387,6 +397,13 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, list_add_tail(&obj->mm_list, &dev_priv->mm.inactive_list); return obj; + +free_out: + kfree(obj->gtt_space); + obj->gtt_space = NULL; +unref_out: + drm_gem_object_unreference(&obj->base); + return NULL; } void diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 88591ef8fa24..d8b56b7d1839 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -138,10 +138,10 @@ static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node) /* * Basic range manager support (drm_mm.c) */ -extern struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm, - unsigned long start, - unsigned long size, - bool atomic); +extern int drm_mm_create_block(struct drm_mm *mm, + struct drm_mm_node *node, + unsigned long start, + unsigned long size); extern struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node, unsigned long size, unsigned alignment, @@ -155,6 +155,7 @@ extern struct drm_mm_node *drm_mm_get_block_range_generic( unsigned long start, unsigned long end, int atomic); + static inline struct drm_mm_node *drm_mm_get_block(struct drm_mm_node *parent, unsigned long size, unsigned alignment) -- cgit v1.2.3-71-gd317 From 338710e7aff3428dc8170a03704a8ae981b58dcd Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 5 Jul 2013 14:41:03 -0700 Subject: drm: Change create block to reserve node With the previous patch we no longer actually create a node, we simply find the correct hole and occupy it. This very well could have been squashed with the last patch, but since I already had David's review, I figured it's easiest to keep it distinct. Also update the users in i915. Conveniently this is the only user of the interface. CC: David Airlie CC: Signed-off-by: Ben Widawsky Acked-by: David Airlie Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_mm.c | 19 ++++++++++--------- drivers/gpu/drm/i915/i915_gem_gtt.c | 8 ++++---- drivers/gpu/drm/i915/i915_gem_stolen.c | 12 +++++++----- include/drm/drm_mm.h | 5 +---- 4 files changed, 22 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 9e8dfbc1955e..52e0ee7f4a6f 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -147,27 +147,27 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, } } -int drm_mm_create_block(struct drm_mm *mm, struct drm_mm_node *node, - unsigned long start, unsigned long size) +int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) { struct drm_mm_node *hole; - unsigned long end = start + size; + unsigned long end = node->start + node->size; unsigned long hole_start; unsigned long hole_end; + BUG_ON(node == NULL); + + /* Find the relevant hole to add our node to */ drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { - if (hole_start > start || hole_end < end) + if (hole_start > node->start || hole_end < end) continue; - node->start = start; - node->size = size; node->mm = mm; node->allocated = 1; INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole->node_list); - if (start == hole_start) { + if (node->start == hole_start) { hole->hole_follows = 0; list_del_init(&hole->hole_stack); } @@ -181,10 +181,11 @@ int drm_mm_create_block(struct drm_mm *mm, struct drm_mm_node *node, return 0; } - WARN(1, "no hole found for block 0x%lx + 0x%lx\n", start, size); + WARN(1, "no hole found for node 0x%lx + 0x%lx\n", + node->start, node->size); return -ENOSPC; } -EXPORT_SYMBOL(drm_mm_create_block); +EXPORT_SYMBOL(drm_mm_reserve_node); struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node, unsigned long size, diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 88180a597c0a..afba7e5e7739 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -640,10 +640,10 @@ void i915_gem_setup_global_gtt(struct drm_device *dev, obj->gtt_offset); continue; } - ret = drm_mm_create_block(&dev_priv->mm.gtt_space, - obj->gtt_space, - obj->gtt_offset, - obj->base.size); + obj->gtt_space->start = obj->gtt_offset; + obj->gtt_space->size = obj->base.size; + ret = drm_mm_reserve_node(&dev_priv->mm.gtt_space, + obj->gtt_space); if (ret) { DRM_DEBUG_KMS("Reservation failed\n"); kfree(obj->gtt_space); diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 2746ff2d846a..4e6dbbb47dfe 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -349,8 +349,9 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, if (!stolen) return NULL; - ret = drm_mm_create_block(&dev_priv->mm.stolen, stolen, stolen_offset, - size); + stolen->start = stolen_offset; + stolen->size = size; + ret = drm_mm_reserve_node(&dev_priv->mm.stolen, stolen); if (ret) { DRM_DEBUG_KMS("failed to allocate stolen space\n"); kfree(stolen); @@ -380,9 +381,10 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, goto unref_out; } - ret = drm_mm_create_block(&dev_priv->mm.gtt_space, - obj->gtt_space, - gtt_offset, size); + obj->gtt_space->start = gtt_offset; + obj->gtt_space->size = size; + ret = drm_mm_reserve_node(&dev_priv->mm.gtt_space, + obj->gtt_space); if (ret) { DRM_DEBUG_KMS("failed to allocate stolen GTT space\n"); goto free_out; diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index d8b56b7d1839..2de91e3da5cc 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -138,10 +138,7 @@ static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node) /* * Basic range manager support (drm_mm.c) */ -extern int drm_mm_create_block(struct drm_mm *mm, - struct drm_mm_node *node, - unsigned long start, - unsigned long size); +extern int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node); extern struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node, unsigned long size, unsigned alignment, -- cgit v1.2.3-71-gd317 From 7d11965ddb9b9b1e0a5d13c58345ada1ccbc663b Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 16 May 2013 17:20:32 +0200 Subject: lib/raid6: add ARM-NEON accelerated syndrome calculation Rebased/reworked a patch contributed by Rob Herring that uses NEON intrinsics to perform the RAID-6 syndrome calculations. It uses the existing unroll.awk code to generate several unrolled versions of which the best performing one is selected at boot time. Signed-off-by: Ard Biesheuvel Acked-by: Nicolas Pitre Cc: hpa@linux.intel.com --- include/linux/raid/pq.h | 5 ++++ lib/raid6/.gitignore | 1 + lib/raid6/Makefile | 40 +++++++++++++++++++++++++ lib/raid6/algos.c | 6 ++++ lib/raid6/neon.c | 58 +++++++++++++++++++++++++++++++++++ lib/raid6/neon.uc | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/raid6/test/Makefile | 26 +++++++++++++++- 7 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 lib/raid6/neon.c create mode 100644 lib/raid6/neon.uc (limited to 'include') diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index 8dfaa2ce2e95..0f424698064f 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -114,6 +114,11 @@ extern const struct raid6_recov_calls raid6_recov_intx1; extern const struct raid6_recov_calls raid6_recov_ssse3; extern const struct raid6_recov_calls raid6_recov_avx2; +extern const struct raid6_calls raid6_neonx1; +extern const struct raid6_calls raid6_neonx2; +extern const struct raid6_calls raid6_neonx4; +extern const struct raid6_calls raid6_neonx8; + /* Algorithm list */ extern const struct raid6_calls * const raid6_algos[]; extern const struct raid6_recov_calls *const raid6_recov_algos[]; diff --git a/lib/raid6/.gitignore b/lib/raid6/.gitignore index 162becacf97c..0a7e494b2bcd 100644 --- a/lib/raid6/.gitignore +++ b/lib/raid6/.gitignore @@ -2,3 +2,4 @@ mktables altivec*.c int*.c tables.c +neon?.c diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile index 9f7c184725d7..b4625787c7ee 100644 --- a/lib/raid6/Makefile +++ b/lib/raid6/Makefile @@ -5,6 +5,7 @@ raid6_pq-y += algos.o recov.o tables.o int1.o int2.o int4.o \ raid6_pq-$(CONFIG_X86) += recov_ssse3.o recov_avx2.o mmx.o sse1.o sse2.o avx2.o raid6_pq-$(CONFIG_ALTIVEC) += altivec1.o altivec2.o altivec4.o altivec8.o +raid6_pq-$(CONFIG_KERNEL_MODE_NEON) += neon.o neon1.o neon2.o neon4.o neon8.o hostprogs-y += mktables @@ -16,6 +17,21 @@ ifeq ($(CONFIG_ALTIVEC),y) altivec_flags := -maltivec -mabi=altivec endif +# The GCC option -ffreestanding is required in order to compile code containing +# ARM/NEON intrinsics in a non C99-compliant environment (such as the kernel) +ifeq ($(CONFIG_KERNEL_MODE_NEON),y) +NEON_FLAGS := -ffreestanding +ifeq ($(ARCH),arm) +NEON_FLAGS += -mfloat-abi=softfp -mfpu=neon +endif +ifeq ($(ARCH),arm64) +CFLAGS_REMOVE_neon1.o += -mgeneral-regs-only +CFLAGS_REMOVE_neon2.o += -mgeneral-regs-only +CFLAGS_REMOVE_neon4.o += -mgeneral-regs-only +CFLAGS_REMOVE_neon8.o += -mgeneral-regs-only +endif +endif + targets += int1.c $(obj)/int1.c: UNROLL := 1 $(obj)/int1.c: $(src)/int.uc $(src)/unroll.awk FORCE @@ -70,6 +86,30 @@ $(obj)/altivec8.c: UNROLL := 8 $(obj)/altivec8.c: $(src)/altivec.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) +CFLAGS_neon1.o += $(NEON_FLAGS) +targets += neon1.c +$(obj)/neon1.c: UNROLL := 1 +$(obj)/neon1.c: $(src)/neon.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) + +CFLAGS_neon2.o += $(NEON_FLAGS) +targets += neon2.c +$(obj)/neon2.c: UNROLL := 2 +$(obj)/neon2.c: $(src)/neon.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) + +CFLAGS_neon4.o += $(NEON_FLAGS) +targets += neon4.c +$(obj)/neon4.c: UNROLL := 4 +$(obj)/neon4.c: $(src)/neon.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) + +CFLAGS_neon8.o += $(NEON_FLAGS) +targets += neon8.c +$(obj)/neon8.c: UNROLL := 8 +$(obj)/neon8.c: $(src)/neon.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) + quiet_cmd_mktable = TABLE $@ cmd_mktable = $(obj)/mktables > $@ || ( rm -f $@ && exit 1 ) diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c index 6d7316fe9f30..74e6f5629dbc 100644 --- a/lib/raid6/algos.c +++ b/lib/raid6/algos.c @@ -70,6 +70,12 @@ const struct raid6_calls * const raid6_algos[] = { &raid6_intx2, &raid6_intx4, &raid6_intx8, +#ifdef CONFIG_KERNEL_MODE_NEON + &raid6_neonx1, + &raid6_neonx2, + &raid6_neonx4, + &raid6_neonx8, +#endif NULL }; diff --git a/lib/raid6/neon.c b/lib/raid6/neon.c new file mode 100644 index 000000000000..36ad4705df1a --- /dev/null +++ b/lib/raid6/neon.c @@ -0,0 +1,58 @@ +/* + * linux/lib/raid6/neon.c - RAID6 syndrome calculation using ARM NEON intrinsics + * + * Copyright (C) 2013 Linaro Ltd + * + * 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 + +#ifdef __KERNEL__ +#include +#else +#define kernel_neon_begin() +#define kernel_neon_end() +#define cpu_has_neon() (1) +#endif + +/* + * There are 2 reasons these wrappers are kept in a separate compilation unit + * from the actual implementations in neonN.c (generated from neon.uc by + * unroll.awk): + * - the actual implementations use NEON intrinsics, and the GCC support header + * (arm_neon.h) is not fully compatible (type wise) with the kernel; + * - the neonN.c files are compiled with -mfpu=neon and optimization enabled, + * and we have to make sure that we never use *any* NEON/VFP instructions + * outside a kernel_neon_begin()/kernel_neon_end() pair. + */ + +#define RAID6_NEON_WRAPPER(_n) \ + static void raid6_neon ## _n ## _gen_syndrome(int disks, \ + size_t bytes, void **ptrs) \ + { \ + void raid6_neon ## _n ## _gen_syndrome_real(int, \ + unsigned long, void**); \ + kernel_neon_begin(); \ + raid6_neon ## _n ## _gen_syndrome_real(disks, \ + (unsigned long)bytes, ptrs); \ + kernel_neon_end(); \ + } \ + struct raid6_calls const raid6_neonx ## _n = { \ + raid6_neon ## _n ## _gen_syndrome, \ + raid6_have_neon, \ + "neonx" #_n, \ + 0 \ + } + +static int raid6_have_neon(void) +{ + return cpu_has_neon(); +} + +RAID6_NEON_WRAPPER(1); +RAID6_NEON_WRAPPER(2); +RAID6_NEON_WRAPPER(4); +RAID6_NEON_WRAPPER(8); diff --git a/lib/raid6/neon.uc b/lib/raid6/neon.uc new file mode 100644 index 000000000000..1b9ed793342d --- /dev/null +++ b/lib/raid6/neon.uc @@ -0,0 +1,80 @@ +/* ----------------------------------------------------------------------- + * + * neon.uc - RAID-6 syndrome calculation using ARM NEON instructions + * + * Copyright (C) 2012 Rob Herring + * + * Based on altivec.uc: + * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved + * + * 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * neon$#.c + * + * $#-way unrolled NEON intrinsics math RAID-6 instruction set + * + * This file is postprocessed using unroll.awk + */ + +#include + +typedef uint8x16_t unative_t; + +#define NBYTES(x) ((unative_t){x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x}) +#define NSIZE sizeof(unative_t) + +/* + * The SHLBYTE() operation shifts each byte left by 1, *not* + * rolling over into the next byte + */ +static inline unative_t SHLBYTE(unative_t v) +{ + return vshlq_n_u8(v, 1); +} + +/* + * The MASK() operation returns 0xFF in any byte for which the high + * bit is 1, 0x00 for any byte for which the high bit is 0. + */ +static inline unative_t MASK(unative_t v) +{ + const uint8x16_t temp = NBYTES(0); + return (unative_t)vcltq_s8((int8x16_t)v, (int8x16_t)temp); +} + +void raid6_neon$#_gen_syndrome_real(int disks, unsigned long bytes, void **ptrs) +{ + uint8_t **dptr = (uint8_t **)ptrs; + uint8_t *p, *q; + int d, z, z0; + + register unative_t wd$$, wq$$, wp$$, w1$$, w2$$; + const unative_t x1d = NBYTES(0x1d); + + z0 = disks - 3; /* Highest data disk */ + p = dptr[z0+1]; /* XOR parity */ + q = dptr[z0+2]; /* RS syndrome */ + + for ( d = 0 ; d < bytes ; d += NSIZE*$# ) { + wq$$ = wp$$ = vld1q_u8(&dptr[z0][d+$$*NSIZE]); + for ( z = z0-1 ; z >= 0 ; z-- ) { + wd$$ = vld1q_u8(&dptr[z][d+$$*NSIZE]); + wp$$ = veorq_u8(wp$$, wd$$); + w2$$ = MASK(wq$$); + w1$$ = SHLBYTE(wq$$); + + w2$$ = vandq_u8(w2$$, x1d); + w1$$ = veorq_u8(w1$$, w2$$); + wq$$ = veorq_u8(w1$$, wd$$); + } + vst1q_u8(&p[d+NSIZE*$$], wp$$); + vst1q_u8(&q[d+NSIZE*$$], wq$$); + } +} diff --git a/lib/raid6/test/Makefile b/lib/raid6/test/Makefile index 087332dbf8aa..28afa1a06e03 100644 --- a/lib/raid6/test/Makefile +++ b/lib/raid6/test/Makefile @@ -22,11 +22,23 @@ ifeq ($(ARCH),x86_64) IS_X86 = yes endif +ifeq ($(ARCH),arm) + CFLAGS += -I../../../arch/arm/include -mfpu=neon + HAS_NEON = yes +endif +ifeq ($(ARCH),arm64) + CFLAGS += -I../../../arch/arm64/include + HAS_NEON = yes +endif + ifeq ($(IS_X86),yes) OBJS += mmx.o sse1.o sse2.o avx2.o recov_ssse3.o recov_avx2.o CFLAGS += $(shell echo "vpbroadcastb %xmm0, %ymm1" | \ gcc -c -x assembler - >&/dev/null && \ rm ./-.o && echo -DCONFIG_AS_AVX2=1) +else ifeq ($(HAS_NEON),yes) + OBJS += neon.o neon1.o neon2.o neon4.o neon8.o + CFLAGS += -DCONFIG_KERNEL_MODE_NEON=1 else HAS_ALTIVEC := $(shell echo -e '\#include \nvector int a;' |\ gcc -c -x c - >&/dev/null && \ @@ -55,6 +67,18 @@ raid6.a: $(OBJS) raid6test: test.c raid6.a $(CC) $(CFLAGS) -o raid6test $^ +neon1.c: neon.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=1 < neon.uc > $@ + +neon2.c: neon.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=2 < neon.uc > $@ + +neon4.c: neon.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=4 < neon.uc > $@ + +neon8.c: neon.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=8 < neon.uc > $@ + altivec1.c: altivec.uc ../unroll.awk $(AWK) ../unroll.awk -vN=1 < altivec.uc > $@ @@ -89,7 +113,7 @@ tables.c: mktables ./mktables > tables.c clean: - rm -f *.o *.a mktables mktables.c *.uc int*.c altivec*.c tables.c raid6test + rm -f *.o *.a mktables mktables.c *.uc int*.c altivec*.c neon*.c tables.c raid6test spotless: clean rm -f *~ -- cgit v1.2.3-71-gd317 From a8855990e382fc81c04187c5fdb48743307baf32 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 9 Jul 2013 22:36:45 +0800 Subject: writeback: Do not sort b_io list only because of block device inode It is very likely that block device inode will be part of BDI dirty list as well. However it doesn't make sence to sort inodes on the b_io list just because of this inode (as it contains buffers all over the device anyway). So save some CPU cycles which is valuable since we hold relatively contented wb->list_lock. Signed-off-by: Jan Kara --- fs/block_dev.c | 2 +- fs/fs-writeback.c | 8 +++++--- include/linux/fs.h | 6 ++++++ 3 files changed, 12 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/fs/block_dev.c b/fs/block_dev.c index 2091db8cdd78..256233098206 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -606,7 +606,7 @@ static struct block_device *bd_acquire(struct inode *inode) return bdev; } -static inline int sb_is_blkdev_sb(struct super_block *sb) +int sb_is_blkdev_sb(struct super_block *sb) { return sb == blockdev_superblock; } diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 3be57189efd5..cabdece4cb39 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -68,7 +68,7 @@ static inline struct backing_dev_info *inode_to_bdi(struct inode *inode) { struct super_block *sb = inode->i_sb; - if (strcmp(sb->s_type->name, "bdev") == 0) + if (sb_is_blkdev_sb(sb)) return inode->i_mapping->backing_dev_info; return sb->s_bdi; @@ -250,11 +250,13 @@ static int move_expired_inodes(struct list_head *delaying_queue, if (work->older_than_this && inode_dirtied_after(inode, *work->older_than_this)) break; + list_move(&inode->i_wb_list, &tmp); + moved++; + if (sb_is_blkdev_sb(inode->i_sb)) + continue; if (sb && sb != inode->i_sb) do_sb_sort = 1; sb = inode->i_sb; - list_move(&inode->i_wb_list, &tmp); - moved++; } /* just one sb in list, splice to dispatch_queue and we're done */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 65c2be22b601..61da2f1c8056 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2058,6 +2058,7 @@ extern struct super_block *freeze_bdev(struct block_device *); extern void emergency_thaw_all(void); extern int thaw_bdev(struct block_device *bdev, struct super_block *sb); extern int fsync_bdev(struct block_device *); +extern int sb_is_blkdev_sb(struct super_block *sb); #else static inline void bd_forget(struct inode *inode) {} static inline int sync_blockdev(struct block_device *bdev) { return 0; } @@ -2077,6 +2078,11 @@ static inline int thaw_bdev(struct block_device *bdev, struct super_block *sb) static inline void iterate_bdevs(void (*f)(struct block_device *, void *), void *arg) { } + +static inline int sb_is_blkdev_sb(struct super_block *sb) +{ + return 0; +} #endif extern int sync_filesystem(struct super_block *); extern const struct file_operations def_blk_fops; -- cgit v1.2.3-71-gd317 From cf870c70a194443f8fc654ddc9d6cfd02c58003b Mon Sep 17 00:00:00 2001 From: "Naveen N. Rao" Date: Wed, 10 Jul 2013 14:57:01 +0530 Subject: mce: acpi/apei: Soft-offline a page on firmware GHES notification If the firmware indicates in GHES error data entry that the error threshold has exceeded for a corrected error event, then we try to soft-offline the page. This could be called in interrupt context, so we queue this up similar to how we handle memory failure scenarios. Signed-off-by: Naveen N. Rao Acked-by: Borislav Petkov Signed-off-by: Tony Luck --- drivers/acpi/apei/ghes.c | 38 +++++++++++++++++++++++++++++--------- include/linux/mm.h | 1 + mm/memory-failure.c | 5 ++++- 3 files changed, 34 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index fcd7d91cec34..a8f362acc8ec 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -409,6 +409,34 @@ static void ghes_clear_estatus(struct ghes *ghes) ghes->flags &= ~GHES_TO_CLEAR; } +static void ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata, int sev) +{ +#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE + unsigned long pfn; + int sec_sev = ghes_severity(gdata->error_severity); + struct cper_sec_mem_err *mem_err; + mem_err = (struct cper_sec_mem_err *)(gdata + 1); + + if (sec_sev == GHES_SEV_CORRECTED && + (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED) && + (mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS)) { + pfn = mem_err->physical_addr >> PAGE_SHIFT; + if (pfn_valid(pfn)) + memory_failure_queue(pfn, 0, MF_SOFT_OFFLINE); + else if (printk_ratelimit()) + pr_warn(FW_WARN GHES_PFX + "Invalid address in generic error data: %#llx\n", + mem_err->physical_addr); + } + if (sev == GHES_SEV_RECOVERABLE && + sec_sev == GHES_SEV_RECOVERABLE && + mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) { + pfn = mem_err->physical_addr >> PAGE_SHIFT; + memory_failure_queue(pfn, 0, 0); + } +#endif +} + static void ghes_do_proc(struct ghes *ghes, const struct acpi_hest_generic_status *estatus) { @@ -428,15 +456,7 @@ static void ghes_do_proc(struct ghes *ghes, apei_mce_report_mem_error(sev == GHES_SEV_CORRECTED, mem_err); #endif -#ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE - if (sev == GHES_SEV_RECOVERABLE && - sec_sev == GHES_SEV_RECOVERABLE && - mem_err->validation_bits & CPER_MEM_VALID_PHYSICAL_ADDRESS) { - unsigned long pfn; - pfn = mem_err->physical_addr >> PAGE_SHIFT; - memory_failure_queue(pfn, 0, 0); - } -#endif + ghes_handle_memory_failure(gdata, sev); } #ifdef CONFIG_ACPI_APEI_PCIEAER else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, diff --git a/include/linux/mm.h b/include/linux/mm.h index e0c8528a41a4..958e9efd02a7 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1784,6 +1784,7 @@ enum mf_flags { MF_COUNT_INCREASED = 1 << 0, MF_ACTION_REQUIRED = 1 << 1, MF_MUST_KILL = 1 << 2, + MF_SOFT_OFFLINE = 1 << 3, }; extern int memory_failure(unsigned long pfn, int trapno, int flags); extern void memory_failure_queue(unsigned long pfn, int trapno, int flags); diff --git a/mm/memory-failure.c b/mm/memory-failure.c index ceb0c7f1932f..0d6717e52ea2 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1286,7 +1286,10 @@ static void memory_failure_work_func(struct work_struct *work) spin_unlock_irqrestore(&mf_cpu->lock, proc_flags); if (!gotten) break; - memory_failure(entry.pfn, entry.trapno, entry.flags); + if (entry.flags & MF_SOFT_OFFLINE) + soft_offline_page(pfn_to_page(entry.pfn), entry.flags); + else + memory_failure(entry.pfn, entry.trapno, entry.flags); } } -- cgit v1.2.3-71-gd317 From a51805efae5dda0da66f79268ffcf0715f9dbea4 Mon Sep 17 00:00:00 2001 From: Michel Lespinasse Date: Mon, 8 Jul 2013 14:23:49 -0700 Subject: lockdep: Introduce lock_acquire_exclusive()/shared() helper macros In lockdep.h, the spinlock/mutex/rwsem/rwlock/lock_map acquire macros have different definitions based on the value of CONFIG_PROVE_LOCKING. We have separate ifdefs for each of these definitions, which seems redundant. Introduce lock_acquire_{exclusive,shared,shared_recursive} helpers which will have different definitions based on CONFIG_PROVE_LOCKING. Then all other helper macros can be defined based on the above ones, which reduces the amount of ifdefined code. Signed-off-by: Michel Lespinasse Signed-off-by: Andrew Morton Signed-off-by: Peter Zijlstra Cc: Oleg Nesterov Cc: Lai Jiangshan Cc: "Srivatsa S. Bhat" Cc: Rusty Russell Cc: Andi Kleen Cc: "Paul E. McKenney" Cc: Steven Rostedt Link: http://lkml.kernel.org/r/20130708212350.6DD1931C15E@corp2gmr1-1.hot.corp.google.com Signed-off-by: Ingo Molnar --- include/linux/lockdep.h | 92 +++++++++++++------------------------------------ 1 file changed, 23 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index f1e877b79ed8..cfc2f119779a 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -365,7 +365,7 @@ extern void lockdep_trace_alloc(gfp_t mask); #define lockdep_recursing(tsk) ((tsk)->lockdep_recursion) -#else /* !LOCKDEP */ +#else /* !CONFIG_LOCKDEP */ static inline void lockdep_off(void) { @@ -479,82 +479,36 @@ static inline void print_irqtrace_events(struct task_struct *curr) * on the per lock-class debug mode: */ -#ifdef CONFIG_DEBUG_LOCK_ALLOC -# ifdef CONFIG_PROVE_LOCKING -# define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) -# define spin_acquire_nest(l, s, t, n, i) lock_acquire(l, s, t, 0, 2, n, i) -# else -# define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) -# define spin_acquire_nest(l, s, t, n, i) lock_acquire(l, s, t, 0, 1, NULL, i) -# endif -# define spin_release(l, n, i) lock_release(l, n, i) +#ifdef CONFIG_PROVE_LOCKING + #define lock_acquire_exclusive(l, s, t, n, i) lock_acquire(l, s, t, 0, 2, n, i) + #define lock_acquire_shared(l, s, t, n, i) lock_acquire(l, s, t, 1, 2, n, i) + #define lock_acquire_shared_recursive(l, s, t, n, i) lock_acquire(l, s, t, 2, 2, n, i) #else -# define spin_acquire(l, s, t, i) do { } while (0) -# define spin_release(l, n, i) do { } while (0) + #define lock_acquire_exclusive(l, s, t, n, i) lock_acquire(l, s, t, 0, 1, n, i) + #define lock_acquire_shared(l, s, t, n, i) lock_acquire(l, s, t, 1, 1, n, i) + #define lock_acquire_shared_recursive(l, s, t, n, i) lock_acquire(l, s, t, 2, 1, n, i) #endif -#ifdef CONFIG_DEBUG_LOCK_ALLOC -# ifdef CONFIG_PROVE_LOCKING -# define rwlock_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) -# define rwlock_acquire_read(l, s, t, i) lock_acquire(l, s, t, 2, 2, NULL, i) -# else -# define rwlock_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) -# define rwlock_acquire_read(l, s, t, i) lock_acquire(l, s, t, 2, 1, NULL, i) -# endif -# define rwlock_release(l, n, i) lock_release(l, n, i) -#else -# define rwlock_acquire(l, s, t, i) do { } while (0) -# define rwlock_acquire_read(l, s, t, i) do { } while (0) -# define rwlock_release(l, n, i) do { } while (0) -#endif +#define spin_acquire(l, s, t, i) lock_acquire_exclusive(l, s, t, NULL, i) +#define spin_acquire_nest(l, s, t, n, i) lock_acquire_exclusive(l, s, t, n, i) +#define spin_release(l, n, i) lock_release(l, n, i) -#ifdef CONFIG_DEBUG_LOCK_ALLOC -# ifdef CONFIG_PROVE_LOCKING -# define mutex_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) -# define mutex_acquire_nest(l, s, t, n, i) lock_acquire(l, s, t, 0, 2, n, i) -# else -# define mutex_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) -# define mutex_acquire_nest(l, s, t, n, i) lock_acquire(l, s, t, 0, 1, n, i) -# endif -# define mutex_release(l, n, i) lock_release(l, n, i) -#else -# define mutex_acquire(l, s, t, i) do { } while (0) -# define mutex_acquire_nest(l, s, t, n, i) do { } while (0) -# define mutex_release(l, n, i) do { } while (0) -#endif +#define rwlock_acquire(l, s, t, i) lock_acquire_exclusive(l, s, t, NULL, i) +#define rwlock_acquire_read(l, s, t, i) lock_acquire_shared_recursive(l, s, t, NULL, i) +#define rwlock_release(l, n, i) lock_release(l, n, i) -#ifdef CONFIG_DEBUG_LOCK_ALLOC -# ifdef CONFIG_PROVE_LOCKING -# define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, NULL, i) -# define rwsem_acquire_nest(l, s, t, n, i) lock_acquire(l, s, t, 0, 2, n, i) -# define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 2, NULL, i) -# else -# define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, NULL, i) -# define rwsem_acquire_nest(l, s, t, n, i) lock_acquire(l, s, t, 0, 1, n, i) -# define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 1, NULL, i) -# endif +#define mutex_acquire(l, s, t, i) lock_acquire_exclusive(l, s, t, NULL, i) +#define mutex_acquire_nest(l, s, t, n, i) lock_acquire_exclusive(l, s, t, n, i) +#define mutex_release(l, n, i) lock_release(l, n, i) + +#define rwsem_acquire(l, s, t, i) lock_acquire_exclusive(l, s, t, NULL, i) +#define rwsem_acquire_nest(l, s, t, n, i) lock_acquire_exclusive(l, s, t, n, i) +#define rwsem_acquire_read(l, s, t, i) lock_acquire_shared(l, s, t, NULL, i) # define rwsem_release(l, n, i) lock_release(l, n, i) -#else -# define rwsem_acquire(l, s, t, i) do { } while (0) -# define rwsem_acquire_nest(l, s, t, n, i) do { } while (0) -# define rwsem_acquire_read(l, s, t, i) do { } while (0) -# define rwsem_release(l, n, i) do { } while (0) -#endif -#ifdef CONFIG_DEBUG_LOCK_ALLOC -# ifdef CONFIG_PROVE_LOCKING -# define lock_map_acquire(l) lock_acquire(l, 0, 0, 0, 2, NULL, _THIS_IP_) -# define lock_map_acquire_read(l) lock_acquire(l, 0, 0, 2, 2, NULL, _THIS_IP_) -# else -# define lock_map_acquire(l) lock_acquire(l, 0, 0, 0, 1, NULL, _THIS_IP_) -# define lock_map_acquire_read(l) lock_acquire(l, 0, 0, 2, 1, NULL, _THIS_IP_) -# endif +#define lock_map_acquire(l) lock_acquire_exclusive(l, 0, 0, NULL, _THIS_IP_) +#define lock_map_acquire_read(l) lock_acquire_shared_recursive(l, 0, 0, NULL, _THIS_IP_) # define lock_map_release(l) lock_release(l, 1, _THIS_IP_) -#else -# define lock_map_acquire(l) do { } while (0) -# define lock_map_acquire_read(l) do { } while (0) -# define lock_map_release(l) do { } while (0) -#endif #ifdef CONFIG_PROVE_LOCKING # define might_lock(lock) \ -- cgit v1.2.3-71-gd317 From 952c63e9512b63220886105cfc791507046fa39a Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 29 Jun 2013 00:24:38 +0800 Subject: ACPI: introduce helper function acpi_has_method() Introduce helper function acpi_has_method() and use it in a number of places to simplify code. [rjw: Changelog] Signed-off-by: Jiang Liu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/battery.c | 11 ++--- drivers/acpi/ec.c | 4 +- drivers/acpi/processor_perflib.c | 22 +++------ drivers/acpi/resource.c | 4 +- drivers/acpi/scan.c | 99 ++++++++++++---------------------------- drivers/acpi/utils.c | 15 ++++++ drivers/acpi/video.c | 39 +++++----------- drivers/acpi/video_detect.c | 19 ++++---- include/acpi/acpi_bus.h | 3 ++ 9 files changed, 80 insertions(+), 136 deletions(-) (limited to 'include') diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 082b4dd252a8..a7627166e18b 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -548,12 +548,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery) static int acpi_battery_init_alarm(struct acpi_battery *battery) { - acpi_status status = AE_OK; - acpi_handle handle = NULL; - /* See if alarms are supported, and if so, set default */ - status = acpi_get_handle(battery->device->handle, "_BTP", &handle); - if (ACPI_FAILURE(status)) { + if (!acpi_has_method(battery->device->handle, "_BTP")) { clear_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags); return 0; } @@ -1066,7 +1062,7 @@ static int acpi_battery_add(struct acpi_device *device) { int result = 0; struct acpi_battery *battery = NULL; - acpi_handle handle; + if (!device) return -EINVAL; battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL); @@ -1078,8 +1074,7 @@ static int acpi_battery_add(struct acpi_device *device) device->driver_data = battery; mutex_init(&battery->lock); mutex_init(&battery->sysfs_lock); - if (ACPI_SUCCESS(acpi_get_handle(battery->device->handle, - "_BIX", &handle))) + if (acpi_has_method(battery->device->handle, "_BIX")) set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags); result = acpi_battery_update(battery); if (result) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 80403c1a89f8..84bf06cec1f2 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1049,10 +1049,8 @@ int __init acpi_ec_ecdt_probe(void) * which needs it, has fake EC._INI method, so use it as flag. * Keep boot_ec struct as it will be needed soon. */ - acpi_handle dummy; if (!dmi_name_in_vendors("ASUS") || - ACPI_FAILURE(acpi_get_handle(boot_ec->handle, "_INI", - &dummy))) + !acpi_has_method(boot_ec->handle, "_INI")) return -ENODEV; } install: diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 1e9732d809bf..51d7948611da 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -164,17 +164,12 @@ static void acpi_processor_ppc_ost(acpi_handle handle, int status) {.type = ACPI_TYPE_INTEGER,}, }; struct acpi_object_list arg_list = {2, params}; - acpi_handle temp; - params[0].integer.value = ACPI_PROCESSOR_NOTIFY_PERFORMANCE; - params[1].integer.value = status; - - /* when there is no _OST , skip it */ - if (ACPI_FAILURE(acpi_get_handle(handle, "_OST", &temp))) - return; - - acpi_evaluate_object(handle, "_OST", &arg_list, NULL); - return; + if (acpi_has_method(handle, "_OST")) { + params[0].integer.value = ACPI_PROCESSOR_NOTIFY_PERFORMANCE; + params[1].integer.value = status; + acpi_evaluate_object(handle, "_OST", &arg_list, NULL); + } } int acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag) @@ -468,14 +463,11 @@ static int acpi_processor_get_performance_states(struct acpi_processor *pr) int acpi_processor_get_performance_info(struct acpi_processor *pr) { int result = 0; - acpi_status status = AE_OK; - acpi_handle handle = NULL; if (!pr || !pr->performance || !pr->handle) return -EINVAL; - status = acpi_get_handle(pr->handle, "_PCT", &handle); - if (ACPI_FAILURE(status)) { + if (!acpi_has_method(pr->handle, "_PCT")) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "ACPI-based processor performance control unavailable\n")); return -ENODEV; @@ -501,7 +493,7 @@ int acpi_processor_get_performance_info(struct acpi_processor *pr) */ update_bios: #ifdef CONFIG_X86 - if (ACPI_SUCCESS(acpi_get_handle(pr->handle, "_PPC", &handle))){ + if (acpi_has_method(pr->handle, "_PPC")) { if(boot_cpu_has(X86_FEATURE_EST)) printk(KERN_WARNING FW_BUG "BIOS needs update for CPU " "frequency support\n"); diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 3322b47ab7ca..b7201fc6f1e1 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -505,14 +505,12 @@ int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, void *preproc_data) { struct res_proc_context c; - acpi_handle not_used; acpi_status status; if (!adev || !adev->handle || !list_empty(list)) return -EINVAL; - status = acpi_get_handle(adev->handle, METHOD_NAME__CRS, ¬_used); - if (ACPI_FAILURE(status)) + if (!acpi_has_method(adev->handle, METHOD_NAME__CRS)) return 0; c.list = list; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 10985573aaa7..ada0b4cf2ba5 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -193,7 +193,6 @@ static acpi_status acpi_bus_online_companions(acpi_handle handle, u32 lvl, static int acpi_scan_hot_remove(struct acpi_device *device) { acpi_handle handle = device->handle; - acpi_handle not_used; struct acpi_object_list arg_list; union acpi_object arg; struct device *errdev; @@ -258,7 +257,7 @@ static int acpi_scan_hot_remove(struct acpi_device *device) put_device(&device->dev); device = NULL; - if (ACPI_SUCCESS(acpi_get_handle(handle, "_LCK", ¬_used))) { + if (acpi_has_method(handle, "_LCK")) { arg_list.count = 1; arg_list.pointer = &arg; arg.type = ACPI_TYPE_INTEGER; @@ -652,7 +651,6 @@ static int acpi_device_setup_files(struct acpi_device *dev) { struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; acpi_status status; - acpi_handle temp; unsigned long long sun; int result = 0; @@ -678,8 +676,7 @@ static int acpi_device_setup_files(struct acpi_device *dev) /* * If device has _STR, 'description' file is created */ - status = acpi_get_handle(dev->handle, "_STR", &temp); - if (ACPI_SUCCESS(status)) { + if (acpi_has_method(dev->handle, "_STR")) { status = acpi_evaluate_object(dev->handle, "_STR", NULL, &buffer); if (ACPI_FAILURE(status)) @@ -709,8 +706,7 @@ static int acpi_device_setup_files(struct acpi_device *dev) * If device has _EJ0, 'eject' file is created that is used to trigger * hot-removal function from userland. */ - status = acpi_get_handle(dev->handle, "_EJ0", &temp); - if (ACPI_SUCCESS(status)) { + if (acpi_has_method(dev->handle, "_EJ0")) { result = device_create_file(&dev->dev, &dev_attr_eject); if (result) return result; @@ -732,9 +728,6 @@ end: static void acpi_device_remove_files(struct acpi_device *dev) { - acpi_status status; - acpi_handle temp; - if (dev->flags.power_manageable) { device_remove_file(&dev->dev, &dev_attr_power_state); if (dev->power.flags.power_resources) @@ -745,20 +738,17 @@ static void acpi_device_remove_files(struct acpi_device *dev) /* * If device has _STR, remove 'description' file */ - status = acpi_get_handle(dev->handle, "_STR", &temp); - if (ACPI_SUCCESS(status)) { + if (acpi_has_method(dev->handle, "_STR")) { kfree(dev->pnp.str_obj); device_remove_file(&dev->dev, &dev_attr_description); } /* * If device has _EJ0, remove 'eject' file. */ - status = acpi_get_handle(dev->handle, "_EJ0", &temp); - if (ACPI_SUCCESS(status)) + if (acpi_has_method(dev->handle, "_EJ0")) device_remove_file(&dev->dev, &dev_attr_eject); - status = acpi_get_handle(dev->handle, "_SUN", &temp); - if (ACPI_SUCCESS(status)) + if (acpi_has_method(dev->handle, "_SUN")) device_remove_file(&dev->dev, &dev_attr_sun); if (dev->pnp.unique_id) @@ -1334,13 +1324,10 @@ static void acpi_bus_set_run_wake_flags(struct acpi_device *device) static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device) { - acpi_handle temp; - acpi_status status = 0; int err; /* Presence of _PRW indicates wake capable */ - status = acpi_get_handle(device->handle, "_PRW", &temp); - if (ACPI_FAILURE(status)) + if (!acpi_has_method(device->handle, "_PRW")) return; err = acpi_bus_extract_wakeup_device_power_package(device->handle, @@ -1370,7 +1357,6 @@ static void acpi_bus_init_power_state(struct acpi_device *device, int state) struct acpi_device_power_state *ps = &device->power.states[state]; char pathname[5] = { '_', 'P', 'R', '0' + state, '\0' }; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - acpi_handle handle; acpi_status status; INIT_LIST_HEAD(&ps->resources); @@ -1393,8 +1379,7 @@ static void acpi_bus_init_power_state(struct acpi_device *device, int state) /* Evaluate "_PSx" to see if we can do explicit sets */ pathname[2] = 'S'; - status = acpi_get_handle(device->handle, pathname, &handle); - if (ACPI_SUCCESS(status)) + if (acpi_has_method(device->handle, pathname)) ps->flags.explicit_set = 1; /* @@ -1413,28 +1398,21 @@ static void acpi_bus_init_power_state(struct acpi_device *device, int state) static void acpi_bus_get_power_flags(struct acpi_device *device) { - acpi_status status; - acpi_handle handle; u32 i; /* Presence of _PS0|_PR0 indicates 'power manageable' */ - status = acpi_get_handle(device->handle, "_PS0", &handle); - if (ACPI_FAILURE(status)) { - status = acpi_get_handle(device->handle, "_PR0", &handle); - if (ACPI_FAILURE(status)) - return; - } + if (!acpi_has_method(device->handle, "_PS0") && + !acpi_has_method(device->handle, "_PR0")) + return; device->flags.power_manageable = 1; /* * Power Management Flags */ - status = acpi_get_handle(device->handle, "_PSC", &handle); - if (ACPI_SUCCESS(status)) + if (acpi_has_method(device->handle, "_PSC")) device->power.flags.explicit_get = 1; - status = acpi_get_handle(device->handle, "_IRC", &handle); - if (ACPI_SUCCESS(status)) + if (acpi_has_method(device->handle, "_IRC")) device->power.flags.inrush_current = 1; /* @@ -1468,28 +1446,18 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) static void acpi_bus_get_flags(struct acpi_device *device) { - acpi_status status = AE_OK; - acpi_handle temp = NULL; - /* Presence of _STA indicates 'dynamic_status' */ - status = acpi_get_handle(device->handle, "_STA", &temp); - if (ACPI_SUCCESS(status)) + if (acpi_has_method(device->handle, "_STA")) device->flags.dynamic_status = 1; /* Presence of _RMV indicates 'removable' */ - status = acpi_get_handle(device->handle, "_RMV", &temp); - if (ACPI_SUCCESS(status)) + if (acpi_has_method(device->handle, "_RMV")) device->flags.removable = 1; /* Presence of _EJD|_EJ0 indicates 'ejectable' */ - status = acpi_get_handle(device->handle, "_EJD", &temp); - if (ACPI_SUCCESS(status)) + if (acpi_has_method(device->handle, "_EJD") || + acpi_has_method(device->handle, "_EJ0")) device->flags.ejectable = 1; - else { - status = acpi_get_handle(device->handle, "_EJ0", &temp); - if (ACPI_SUCCESS(status)) - device->flags.ejectable = 1; - } } static void acpi_device_get_busid(struct acpi_device *device) @@ -1538,27 +1506,24 @@ static void acpi_device_get_busid(struct acpi_device *device) */ static int acpi_bay_match(acpi_handle handle) { - acpi_status status; - acpi_handle tmp; acpi_handle phandle; - status = acpi_get_handle(handle, "_EJ0", &tmp); - if (ACPI_FAILURE(status)) + if (!acpi_has_method(handle, "_EJ0")) return -ENODEV; - if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) || - (ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) || - (ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) || - (ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp)))) + if (acpi_has_method(handle, "_GTF") || + acpi_has_method(handle, "_GTM") || + acpi_has_method(handle, "_STM") || + acpi_has_method(handle, "_SDD")) return 0; if (acpi_get_parent(handle, &phandle)) return -ENODEV; - if ((ACPI_SUCCESS(acpi_get_handle(phandle, "_GTF", &tmp))) || - (ACPI_SUCCESS(acpi_get_handle(phandle, "_GTM", &tmp))) || - (ACPI_SUCCESS(acpi_get_handle(phandle, "_STM", &tmp))) || - (ACPI_SUCCESS(acpi_get_handle(phandle, "_SDD", &tmp)))) + if (acpi_has_method(phandle, "_GTF") || + acpi_has_method(phandle, "_GTM") || + acpi_has_method(phandle, "_STM") || + acpi_has_method(phandle, "_SDD")) return 0; return -ENODEV; @@ -1610,7 +1575,6 @@ static void acpi_add_id(struct acpi_device_pnp *pnp, const char *dev_id) */ static int acpi_ibm_smbus_match(acpi_handle handle) { - acpi_handle h_dummy; struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; int result; @@ -1629,9 +1593,9 @@ static int acpi_ibm_smbus_match(acpi_handle handle) /* Does it have the necessary (but misnamed) methods? */ result = -ENODEV; - if (ACPI_SUCCESS(acpi_get_handle(handle, "SBI", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(handle, "SBR", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(handle, "SBW", &h_dummy))) + if (acpi_has_method(handle, "SBI") && + acpi_has_method(handle, "SBR") && + acpi_has_method(handle, "SBW")) result = 0; out: kfree(path.pointer); @@ -1898,7 +1862,6 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, struct acpi_device *device = NULL; int type; unsigned long long sta; - acpi_status status; int result; acpi_bus_get_device(handle, &device); @@ -1919,10 +1882,8 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, if (!(sta & ACPI_STA_DEVICE_PRESENT) && !(sta & ACPI_STA_DEVICE_FUNCTIONING)) { struct acpi_device_wakeup wakeup; - acpi_handle temp; - status = acpi_get_handle(handle, "_PRW", &temp); - if (ACPI_SUCCESS(status)) { + if (acpi_has_method(handle, "_PRW")) { acpi_bus_extract_wakeup_device_power_package(handle, &wakeup); acpi_power_resources_list_free(&wakeup.resources); diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 744371304313..b08d97376f84 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -495,3 +495,18 @@ acpi_handle_printk(const char *level, acpi_handle handle, const char *fmt, ...) kfree(buffer.pointer); } EXPORT_SYMBOL(acpi_handle_printk); + +/** + * acpi_has_method: Check whether @handle has a method named @name + * @handle: ACPI device handle + * @name: name of object or method + * + * Check whether @handle has a method named @name. + */ +bool acpi_has_method(acpi_handle handle, char *name) +{ + acpi_handle tmp; + + return ACPI_SUCCESS(acpi_get_handle(handle, name, &tmp)); +} +EXPORT_SYMBOL(acpi_has_method); diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 5d7075d25700..a84533e67b9d 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -875,28 +875,21 @@ out: static void acpi_video_device_find_cap(struct acpi_video_device *device) { - acpi_handle h_dummy1; - - if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) { + if (acpi_has_method(device->dev->handle, "_ADR")) device->cap._ADR = 1; - } - if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCL", &h_dummy1))) { + if (acpi_has_method(device->dev->handle, "_BCL")) device->cap._BCL = 1; - } - if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) { + if (acpi_has_method(device->dev->handle, "_BCM")) device->cap._BCM = 1; - } - if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1))) + if (acpi_has_method(device->dev->handle, "_BQC")) { device->cap._BQC = 1; - else if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCQ", - &h_dummy1))) { + } else if (acpi_has_method(device->dev->handle, "_BCQ")) { printk(KERN_WARNING FW_BUG "_BCQ is used instead of _BQC\n"); device->cap._BCQ = 1; } - if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) { + if (acpi_has_method(device->dev->handle, "_DDC")) device->cap._DDC = 1; - } if (acpi_video_backlight_support()) { struct backlight_properties props; @@ -984,26 +977,18 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) static void acpi_video_bus_find_cap(struct acpi_video_bus *video) { - acpi_handle h_dummy1; - - if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) { + if (acpi_has_method(video->device->handle, "_DOS")) video->cap._DOS = 1; - } - if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOD", &h_dummy1))) { + if (acpi_has_method(video->device->handle, "_DOD")) video->cap._DOD = 1; - } - if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_ROM", &h_dummy1))) { + if (acpi_has_method(video->device->handle, "_ROM")) video->cap._ROM = 1; - } - if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_GPD", &h_dummy1))) { + if (acpi_has_method(video->device->handle, "_GPD")) video->cap._GPD = 1; - } - if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_SPD", &h_dummy1))) { + if (acpi_has_method(video->device->handle, "_SPD")) video->cap._SPD = 1; - } - if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_VPO", &h_dummy1))) { + if (acpi_has_method(video->device->handle, "_VPO")) video->cap._VPO = 1; - } } /* diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index e6bd910bc6ed..ddefa5f954e2 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -51,14 +51,13 @@ acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context, void **retyurn_value) { long *cap = context; - acpi_handle h_dummy; - if (ACPI_SUCCESS(acpi_get_handle(handle, "_BCM", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(handle, "_BCL", &h_dummy))) { + if (acpi_has_method(handle, "_BCM") && + acpi_has_method(handle, "_BCL")) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found generic backlight " "support\n")); *cap |= ACPI_VIDEO_BACKLIGHT; - if (ACPI_FAILURE(acpi_get_handle(handle, "_BQC", &h_dummy))) + if (!acpi_has_method(handle, "_BQC")) printk(KERN_WARNING FW_BUG PREFIX "No _BQC method, " "cannot determine initial brightness\n"); /* We have backlight support, no need to scan further */ @@ -77,22 +76,20 @@ acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context, */ long acpi_is_video_device(acpi_handle handle) { - acpi_handle h_dummy; long video_caps = 0; /* Is this device able to support video switching ? */ - if (ACPI_SUCCESS(acpi_get_handle(handle, "_DOD", &h_dummy)) || - ACPI_SUCCESS(acpi_get_handle(handle, "_DOS", &h_dummy))) + if (acpi_has_method(handle, "_DOD") || acpi_has_method(handle, "_DOS")) video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING; /* Is this device able to retrieve a video ROM ? */ - if (ACPI_SUCCESS(acpi_get_handle(handle, "_ROM", &h_dummy))) + if (acpi_has_method(handle, "_ROM")) video_caps |= ACPI_VIDEO_ROM_AVAILABLE; /* Is this device able to configure which video head to be POSTed ? */ - if (ACPI_SUCCESS(acpi_get_handle(handle, "_VPO", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(handle, "_GPD", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(handle, "_SPD", &h_dummy))) + if (acpi_has_method(handle, "_VPO") && + acpi_has_method(handle, "_GPD") && + acpi_has_method(handle, "_SPD")) video_caps |= ACPI_VIDEO_DEVICE_POSTING; /* Only check for backlight functionality if one of the above hit. */ diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 56e6b68c8d2f..62b2811bade4 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -56,6 +56,9 @@ acpi_evaluate_hotplug_ost(acpi_handle handle, u32 source_event, acpi_status acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld); + +bool acpi_has_method(acpi_handle handle, char *name); + #ifdef CONFIG_ACPI #include -- cgit v1.2.3-71-gd317 From 0db98202605c3d32e023d43c30b5bd878f520976 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 29 Jun 2013 00:24:39 +0800 Subject: ACPI: introduce helper function acpi_execute_simple_method() Introduce helper function acpi_execute_simple_method() and use it in a number of places to simplify code. [rjw: Changelog] Signed-off-by: Jiang Liu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/battery.c | 8 ++------ drivers/acpi/bus.c | 6 +----- drivers/acpi/power.c | 4 +--- drivers/acpi/sleep.c | 7 ++----- drivers/acpi/thermal.c | 18 ++++-------------- drivers/acpi/utils.c | 12 ++++++++++++ drivers/acpi/video.c | 17 +++++------------ include/acpi/acpi_bus.h | 2 ++ 8 files changed, 29 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index a7627166e18b..74669ac4c615 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -525,18 +525,14 @@ static int acpi_battery_get_state(struct acpi_battery *battery) static int acpi_battery_set_alarm(struct acpi_battery *battery) { acpi_status status = 0; - union acpi_object arg0 = { .type = ACPI_TYPE_INTEGER }; - struct acpi_object_list arg_list = { 1, &arg0 }; if (!acpi_battery_present(battery) || !test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags)) return -ENODEV; - arg0.integer.value = battery->alarm; - mutex_lock(&battery->lock); - status = acpi_evaluate_object(battery->device->handle, "_BTP", - &arg_list, NULL); + status = acpi_execute_simple_method(battery->device->handle, "_BTP", + battery->alarm); mutex_unlock(&battery->lock); if (ACPI_FAILURE(status)) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index a5bb33bab448..a5a032e2344e 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -593,8 +593,6 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) static int __init acpi_bus_init_irq(void) { acpi_status status; - union acpi_object arg = { ACPI_TYPE_INTEGER }; - struct acpi_object_list arg_list = { 1, &arg }; char *message = NULL; @@ -623,9 +621,7 @@ static int __init acpi_bus_init_irq(void) printk(KERN_INFO PREFIX "Using %s for interrupt routing\n", message); - arg.integer.value = acpi_irq_model; - - status = acpi_evaluate_object(NULL, "\\_PIC", &arg_list, NULL); + status = acpi_execute_simple_method(NULL, "\\_PIC", acpi_irq_model); if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PIC")); return -ENODEV; diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 5c28c894c0fc..1460c88a7c0e 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -637,9 +637,7 @@ int acpi_device_sleep_wake(struct acpi_device *dev, } /* Execute _PSW */ - arg_list.count = 1; - in_arg[0].integer.value = enable; - status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL); + status = acpi_execute_simple_method(dev->handle, "_PSW", enable); if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { printk(KERN_ERR PREFIX "_PSW execution failed\n"); dev->wakeup.flags.valid = 0; diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 187ab61889e6..81b0f03d97db 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -31,12 +31,9 @@ static u8 sleep_states[ACPI_S_STATE_COUNT]; static void acpi_sleep_tts_switch(u32 acpi_state) { - union acpi_object in_arg = { ACPI_TYPE_INTEGER }; - struct acpi_object_list arg_list = { 1, &in_arg }; - acpi_status status = AE_OK; + acpi_status status; - in_arg.integer.value = acpi_state; - status = acpi_evaluate_object(NULL, "\\_TTS", &arg_list, NULL); + status = acpi_execute_simple_method(NULL, "\\_TTS", acpi_state); if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { /* * OS can't evaluate the _TTS object correctly. Some warning diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index a33821ca3895..94523c79dc5f 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -239,26 +239,16 @@ static int acpi_thermal_get_polling_frequency(struct acpi_thermal *tz) static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode) { - acpi_status status = AE_OK; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list arg_list = { 1, &arg0 }; - acpi_handle handle = NULL; - - if (!tz) return -EINVAL; - status = acpi_get_handle(tz->device->handle, "_SCP", &handle); - if (ACPI_FAILURE(status)) { + if (!acpi_has_method(tz->device->handle, "_SCP")) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_SCP not present\n")); return -ENODEV; - } - - arg0.integer.value = mode; - - status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); - if (ACPI_FAILURE(status)) + } else if (ACPI_FAILURE(acpi_execute_simple_method(tz->device->handle, + "_SCP", mode))) { return -ENODEV; + } return 0; } diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index b08d97376f84..87b85882b5df 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -510,3 +510,15 @@ bool acpi_has_method(acpi_handle handle, char *name) return ACPI_SUCCESS(acpi_get_handle(handle, name, &tmp)); } EXPORT_SYMBOL(acpi_has_method); + +acpi_status acpi_execute_simple_method(acpi_handle handle, char *method, + u64 arg) +{ + union acpi_object obj = { .type = ACPI_TYPE_INTEGER }; + struct acpi_object_list arg_list = { .count = 1, .pointer = &obj, }; + + obj.integer.value = arg; + + return acpi_evaluate_object(handle, method, &arg_list, NULL); +} +EXPORT_SYMBOL(acpi_execute_simple_method); diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index a84533e67b9d..b862c7f74941 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -353,14 +353,10 @@ static int acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level) { int status; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; int state; - arg0.integer.value = level; - - status = acpi_evaluate_object(device->dev->handle, "_BCM", - &args, NULL); + status = acpi_execute_simple_method(device->dev->handle, + "_BCM", level); if (ACPI_FAILURE(status)) { ACPI_ERROR((AE_INFO, "Evaluating _BCM failed")); return -EIO; @@ -628,18 +624,15 @@ static int acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag) { acpi_status status; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; if (!video->cap._DOS) return 0; if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) return -EINVAL; - arg0.integer.value = (lcd_flag << 2) | bios_flag; - video->dos_setting = arg0.integer.value; - status = acpi_evaluate_object(video->device->handle, "_DOS", - &args, NULL); + video->dos_setting = (lcd_flag << 2) | bios_flag; + status = acpi_execute_simple_method(video->device->handle, "_DOS", + (lcd_flag << 2) | bios_flag); if (ACPI_FAILURE(status)) return -EIO; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 62b2811bade4..e3862587b9de 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -58,6 +58,8 @@ acpi_status acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld); bool acpi_has_method(acpi_handle handle, char *name); +acpi_status acpi_execute_simple_method(acpi_handle handle, char *method, + u64 arg); #ifdef CONFIG_ACPI -- cgit v1.2.3-71-gd317 From 7d2421f84b445dc48c68d33911f1fd6ce6853ee3 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 29 Jun 2013 00:24:40 +0800 Subject: ACPI: introduce two helper functions for _EJ0 and _LCK Introduce two helper functions, acpi_evaluate_ej0() and acpi_evaluate_lck(), that will execute the _EJ0 and _LCK ACPI control methods, respectively, and use them to simplify the ACPI scan code. [rjw: Changelog] Signed-off-by: Jiang Liu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 31 ++++++------------------------- drivers/acpi/utils.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/acpi/acpi_bus.h | 2 ++ 3 files changed, 51 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ada0b4cf2ba5..4c25c3b7ef81 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -193,8 +193,6 @@ static acpi_status acpi_bus_online_companions(acpi_handle handle, u32 lvl, static int acpi_scan_hot_remove(struct acpi_device *device) { acpi_handle handle = device->handle; - struct acpi_object_list arg_list; - union acpi_object arg; struct device *errdev; acpi_status status; unsigned long long sta; @@ -257,32 +255,15 @@ static int acpi_scan_hot_remove(struct acpi_device *device) put_device(&device->dev); device = NULL; - if (acpi_has_method(handle, "_LCK")) { - arg_list.count = 1; - arg_list.pointer = &arg; - arg.type = ACPI_TYPE_INTEGER; - arg.integer.value = 0; - acpi_evaluate_object(handle, "_LCK", &arg_list, NULL); - } - - arg_list.count = 1; - arg_list.pointer = &arg; - arg.type = ACPI_TYPE_INTEGER; - arg.integer.value = 1; - + acpi_evaluate_lck(handle, 0); /* * TBD: _EJD support. */ - status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); - if (ACPI_FAILURE(status)) { - if (status == AE_NOT_FOUND) { - return -ENODEV; - } else { - acpi_handle_warn(handle, "Eject failed (0x%x)\n", - status); - return -EIO; - } - } + status = acpi_evaluate_ej0(handle); + if (status == AE_NOT_FOUND) + return -ENODEV; + else if (ACPI_FAILURE(status)) + return -EIO; /* * Verify if eject was indeed successful. If not, log an error diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 87b85882b5df..552248b0005b 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -522,3 +522,46 @@ acpi_status acpi_execute_simple_method(acpi_handle handle, char *method, return acpi_evaluate_object(handle, method, &arg_list, NULL); } EXPORT_SYMBOL(acpi_execute_simple_method); + +/** + * acpi_evaluate_ej0: Evaluate _EJ0 method for hotplug operations + * @handle: ACPI device handle + * + * Evaluate device's _EJ0 method for hotplug operations. + */ +acpi_status acpi_evaluate_ej0(acpi_handle handle) +{ + acpi_status status; + + status = acpi_execute_simple_method(handle, "_EJ0", 1); + if (status == AE_NOT_FOUND) + acpi_handle_warn(handle, "No _EJ0 support for device\n"); + else if (ACPI_FAILURE(status)) + acpi_handle_warn(handle, "Eject failed (0x%x)\n", status); + + return status; +} + +/** + * acpi_evaluate_lck: Evaluate _LCK method to lock/unlock device + * @handle: ACPI device handle + * @lock: lock device if non-zero, otherwise unlock device + * + * Evaluate device's _LCK method if present to lock/unlock device + */ +acpi_status acpi_evaluate_lck(acpi_handle handle, int lock) +{ + acpi_status status; + + status = acpi_execute_simple_method(handle, "_LCK", !!lock); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + if (lock) + acpi_handle_warn(handle, + "Locking device failed (0x%x)\n", status); + else + acpi_handle_warn(handle, + "Unlocking device failed (0x%x)\n", status); + } + + return status; +} diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index e3862587b9de..f499157b04e8 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -60,6 +60,8 @@ acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld bool acpi_has_method(acpi_handle handle, char *name); acpi_status acpi_execute_simple_method(acpi_handle handle, char *method, u64 arg); +acpi_status acpi_evaluate_ej0(acpi_handle handle); +acpi_status acpi_evaluate_lck(acpi_handle handle, int lock); #ifdef CONFIG_ACPI -- cgit v1.2.3-71-gd317 From ebf4df8db0e7e5db9f7fca5fcd0c2b90ac954385 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sat, 29 Jun 2013 00:24:41 +0800 Subject: ACPI: Export acpi_(bay)|(dock)_match() from scan.c Functions acpi_dock_match() and acpi_bay_match() in scan.c can be shared with dock.c to reduce code duplication, so export them as global functions. Also add a new function acpi_ata_match() to check whether an ACPI device object represents an ATA device. [rjw: Changelog] Signed-off-by: Jiang Liu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 77 +++++++++++++++++++++++-------------------------- include/acpi/acpi_bus.h | 3 ++ 2 files changed, 39 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 4c25c3b7ef81..62e2055e8806 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1479,44 +1479,46 @@ static void acpi_device_get_busid(struct acpi_device *device) } } +/* + * acpi_ata_match - see if an acpi object is an ATA device + * + * If an acpi object has one of the ACPI ATA methods defined, + * then we can safely call it an ATA device. + */ +bool acpi_ata_match(acpi_handle handle) +{ + return acpi_has_method(handle, "_GTF") || + acpi_has_method(handle, "_GTM") || + acpi_has_method(handle, "_STM") || + acpi_has_method(handle, "_SDD"); +} + /* * acpi_bay_match - see if an acpi object is an ejectable driver bay * * If an acpi object is ejectable and has one of the ACPI ATA methods defined, * then we can safely call it an ejectable drive bay */ -static int acpi_bay_match(acpi_handle handle) +bool acpi_bay_match(acpi_handle handle) { acpi_handle phandle; if (!acpi_has_method(handle, "_EJ0")) - return -ENODEV; + return false; + if (acpi_ata_match(handle)) + return true; + if (ACPI_FAILURE(acpi_get_parent(handle, &phandle))) + return false; - if (acpi_has_method(handle, "_GTF") || - acpi_has_method(handle, "_GTM") || - acpi_has_method(handle, "_STM") || - acpi_has_method(handle, "_SDD")) - return 0; - - if (acpi_get_parent(handle, &phandle)) - return -ENODEV; - - if (acpi_has_method(phandle, "_GTF") || - acpi_has_method(phandle, "_GTM") || - acpi_has_method(phandle, "_STM") || - acpi_has_method(phandle, "_SDD")) - return 0; - - return -ENODEV; + return acpi_ata_match(phandle); } /* * acpi_dock_match - see if an acpi object has a _DCK method */ -static int acpi_dock_match(acpi_handle handle) +bool acpi_dock_match(acpi_handle handle) { - acpi_handle tmp; - return acpi_get_handle(handle, "_DCK", &tmp); + return acpi_has_method(handle, "_DCK"); } const char *acpi_device_hid(struct acpi_device *device) @@ -1554,33 +1556,26 @@ static void acpi_add_id(struct acpi_device_pnp *pnp, const char *dev_id) * lacks the SMBUS01 HID and the methods do not have the necessary "_" * prefix. Work around this. */ -static int acpi_ibm_smbus_match(acpi_handle handle) +static bool acpi_ibm_smbus_match(acpi_handle handle) { - struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; - int result; + char node_name[ACPI_PATH_SEGMENT_LENGTH]; + struct acpi_buffer path = { sizeof(node_name), node_name }; if (!dmi_name_in_vendors("IBM")) - return -ENODEV; + return false; /* Look for SMBS object */ - result = acpi_get_name(handle, ACPI_SINGLE_NAME, &path); - if (result) - return result; - - if (strcmp("SMBS", path.pointer)) { - result = -ENODEV; - goto out; - } + if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &path)) || + strcmp("SMBS", path.pointer)) + return false; /* Does it have the necessary (but misnamed) methods? */ - result = -ENODEV; if (acpi_has_method(handle, "SBI") && acpi_has_method(handle, "SBR") && acpi_has_method(handle, "SBW")) - result = 0; -out: - kfree(path.pointer); - return result; + return true; + + return false; } static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, @@ -1628,11 +1623,11 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, */ if (acpi_is_video_device(handle)) acpi_add_id(pnp, ACPI_VIDEO_HID); - else if (ACPI_SUCCESS(acpi_bay_match(handle))) + else if (acpi_bay_match(handle)) acpi_add_id(pnp, ACPI_BAY_HID); - else if (ACPI_SUCCESS(acpi_dock_match(handle))) + else if (acpi_dock_match(handle)) acpi_add_id(pnp, ACPI_DOCK_HID); - else if (!acpi_ibm_smbus_match(handle)) + else if (acpi_ibm_smbus_match(handle)) acpi_add_id(pnp, ACPI_SMBUS_IBM_HID); else if (list_empty(&pnp->ids) && handle == ACPI_ROOT_OBJECT) { acpi_add_id(pnp, ACPI_BUS_HID); /* \_SB, LNXSYBUS */ diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index f499157b04e8..facc0ba6dd21 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -62,6 +62,9 @@ acpi_status acpi_execute_simple_method(acpi_handle handle, char *method, u64 arg); acpi_status acpi_evaluate_ej0(acpi_handle handle); acpi_status acpi_evaluate_lck(acpi_handle handle, int lock); +bool acpi_ata_match(acpi_handle handle); +bool acpi_bay_match(acpi_handle handle); +bool acpi_dock_match(acpi_handle handle); #ifdef CONFIG_ACPI -- cgit v1.2.3-71-gd317 From f716fc2ac037c45a6c641eb9f20ec602e8d04e14 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 30 Jun 2013 23:49:55 +0200 Subject: ACPI: Drop ACPI bus notifier call chain There are no users of the ACPI bus notifier call chain, acpi_bus_notify_list, any more, so drop it. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 16 ---------------- include/acpi/acpi_bus.h | 2 -- 2 files changed, 18 deletions(-) (limited to 'include') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index a5a032e2344e..6fd27a9abcda 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -499,19 +499,6 @@ static void acpi_bus_check_scope(acpi_handle handle) */ } -static BLOCKING_NOTIFIER_HEAD(acpi_bus_notify_list); -int register_acpi_bus_notifier(struct notifier_block *nb) -{ - return blocking_notifier_chain_register(&acpi_bus_notify_list, nb); -} -EXPORT_SYMBOL_GPL(register_acpi_bus_notifier); - -void unregister_acpi_bus_notifier(struct notifier_block *nb) -{ - blocking_notifier_chain_unregister(&acpi_bus_notify_list, nb); -} -EXPORT_SYMBOL_GPL(unregister_acpi_bus_notifier); - /** * acpi_bus_notify * --------------- @@ -525,9 +512,6 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n", type, handle)); - blocking_notifier_call_chain(&acpi_bus_notify_list, - type, (void *)handle); - switch (type) { case ACPI_NOTIFY_BUS_CHECK: diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index facc0ba6dd21..71f3fd429fd1 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -366,8 +366,6 @@ extern int acpi_notifier_call_chain(struct acpi_device *, u32, u32); extern int register_acpi_notifier(struct notifier_block *); extern int unregister_acpi_notifier(struct notifier_block *); -extern int register_acpi_bus_notifier(struct notifier_block *nb); -extern void unregister_acpi_bus_notifier(struct notifier_block *nb); /* * External Functions */ -- cgit v1.2.3-71-gd317 From e6c215f15a1cbf4cdd6996d95fba0c0d6c0f3ccc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 5 Jul 2013 03:02:25 +0200 Subject: ACPI / dock: Do not check CONFIG_ACPI_DOCK_MODULE Since commit 94add0f (ACPI / dock: Initialize ACPI dock subsystem upfront) the ACPI dock driver cannot be a module, so CONFIG_ACPI_DOCK_MODULE is never set. For this reason, simplify the preprocessor conditional in include/acpi/acpi_drivers.h referring to that sybbol unnecessarily. Signed-off-by: Rafael J. Wysocki --- include/acpi/acpi_drivers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index b420939f5eb5..0cf85786ed21 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -117,7 +117,7 @@ struct acpi_dock_ops { acpi_notify_handler uevent; }; -#if defined(CONFIG_ACPI_DOCK) || defined(CONFIG_ACPI_DOCK_MODULE) +#ifdef CONFIG_ACPI_DOCK extern int is_dock_device(acpi_handle handle); extern int register_dock_notifier(struct notifier_block *nb); extern void unregister_dock_notifier(struct notifier_block *nb); @@ -150,6 +150,6 @@ static inline int register_hotplug_dock_device(acpi_handle handle, static inline void unregister_hotplug_dock_device(acpi_handle handle) { } -#endif +#endif /* CONFIG_ACPI_DOCK */ #endif /*__ACPI_DRIVERS_H__*/ -- cgit v1.2.3-71-gd317 From f09ce741a03ad7de591aa47e760fbeee28567b63 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 5 Jul 2013 03:03:25 +0200 Subject: ACPI / dock / PCI: Drop ACPI dock notifier chain The only user of the ACPI dock notifier chain is the ACPI-based PCI hotplug (acpiphp) driver that uses it to carry out post-dock fixups needed by some systems with broken _DCK. However, it is not necessary to use a separate notifier chain for that, as it can be simply replaced with a new callback in struct acpi_dock_ops. For this reason, add a new .fixup() callback to struct acpi_dock_ops and make hotplug_dock_devices() execute it for all dock devices with hotplug operations registered. Accordingly, make acpiphp point that callback to the function carrying out the post-dock fixups and do not register a separate dock notifier for each device registering dock operations. Finally, drop the ACPI dock notifier chain that has no more users. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/dock.c | 66 +++++++++++++++----------------------- drivers/pci/hotplug/acpiphp.h | 1 - drivers/pci/hotplug/acpiphp_glue.c | 18 +++-------- include/acpi/acpi_drivers.h | 10 +----- 4 files changed, 30 insertions(+), 65 deletions(-) (limited to 'include') diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 3e20b13fa272..c89a9c3b48b4 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -51,8 +51,6 @@ MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to " " the driver to wait for userspace to write the undock sysfs file " " before undocking"); -static struct atomic_notifier_head dock_notifier_list; - static const struct acpi_device_id dock_device_ids[] = { {"LNXDOCK", 0}, {"", 0}, @@ -89,6 +87,12 @@ struct dock_dependent_device { #define DOCK_EVENT 3 #define UNDOCK_EVENT 2 +enum dock_callback_type { + DOCK_CALL_HANDLER, + DOCK_CALL_FIXUP, + DOCK_CALL_UEVENT, +}; + /***************************************************************************** * Dock Dependent device functions * *****************************************************************************/ @@ -179,7 +183,7 @@ static void dock_release_hotplug(struct dock_dependent_device *dd) } static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, - bool uevent) + enum dock_callback_type cb_type) { acpi_notify_handler cb = NULL; bool run = false; @@ -189,8 +193,18 @@ static void dock_hotplug_event(struct dock_dependent_device *dd, u32 event, if (dd->hp_context) { run = true; dd->hp_refcount++; - if (dd->hp_ops) - cb = uevent ? dd->hp_ops->uevent : dd->hp_ops->handler; + if (dd->hp_ops) { + switch (cb_type) { + case DOCK_CALL_FIXUP: + cb = dd->hp_ops->fixup; + break; + case DOCK_CALL_UEVENT: + cb = dd->hp_ops->uevent; + break; + default: + cb = dd->hp_ops->handler; + } + } } mutex_unlock(&hotplug_lock); @@ -372,9 +386,13 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event) { struct dock_dependent_device *dd; + /* Call driver specific post-dock fixups. */ + list_for_each_entry(dd, &ds->dependent_devices, list) + dock_hotplug_event(dd, event, DOCK_CALL_FIXUP); + /* Call driver specific hotplug functions. */ list_for_each_entry(dd, &ds->dependent_devices, list) - dock_hotplug_event(dd, event, false); + dock_hotplug_event(dd, event, DOCK_CALL_HANDLER); /* * Now make sure that an acpi_device is created for each dependent @@ -405,7 +423,7 @@ static void dock_event(struct dock_station *ds, u32 event, int num) kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); list_for_each_entry(dd, &ds->dependent_devices, list) - dock_hotplug_event(dd, event, true); + dock_hotplug_event(dd, event, DOCK_CALL_UEVENT); if (num != DOCK_EVENT) kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); @@ -487,37 +505,6 @@ static int dock_in_progress(struct dock_station *ds) return 0; } -/** - * register_dock_notifier - add yourself to the dock notifier list - * @nb: the callers notifier block - * - * If a driver wishes to be notified about dock events, they can - * use this function to put a notifier block on the dock notifier list. - * this notifier call chain will be called after a dock event, but - * before hotplugging any new devices. - */ -int register_dock_notifier(struct notifier_block *nb) -{ - if (!dock_station_count) - return -ENODEV; - - return atomic_notifier_chain_register(&dock_notifier_list, nb); -} -EXPORT_SYMBOL_GPL(register_dock_notifier); - -/** - * unregister_dock_notifier - remove yourself from the dock notifier list - * @nb: the callers notifier block - */ -void unregister_dock_notifier(struct notifier_block *nb) -{ - if (!dock_station_count) - return; - - atomic_notifier_chain_unregister(&dock_notifier_list, nb); -} -EXPORT_SYMBOL_GPL(unregister_dock_notifier); - /** * register_hotplug_dock_device - register a hotplug function * @handle: the handle of the device @@ -658,8 +645,6 @@ static void dock_notify(struct dock_station *ds, u32 event) complete_dock(ds); break; } - atomic_notifier_call_chain(&dock_notifier_list, - event, NULL); hotplug_dock_devices(ds, event); complete_dock(ds); dock_event(ds, event, DOCK_EVENT); @@ -945,7 +930,6 @@ void __init acpi_dock_init(void) return; } - ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); pr_info(PREFIX "%s: %d docks/bays found\n", ACPI_DOCK_DRIVER_DESCRIPTION, dock_station_count); } diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 6fdd49c6f0b9..6c781edabcc6 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -122,7 +122,6 @@ struct acpiphp_func { struct acpiphp_slot *slot; /* parent */ struct list_head sibling; - struct notifier_block nb; acpi_handle handle; u8 function; /* pci function# */ diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index a0a7133a1d12..8bfad0dc29ab 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -119,15 +119,14 @@ static void free_bridge(struct kref *kref) * TBD - figure out a way to only call fixups for * systems that require them. */ -static int post_dock_fixups(struct notifier_block *nb, unsigned long val, - void *v) +static void post_dock_fixups(acpi_handle not_used, u32 event, void *data) { - struct acpiphp_func *func = container_of(nb, struct acpiphp_func, nb); + struct acpiphp_func *func = data; struct pci_bus *bus = func->slot->bridge->pci_bus; u32 buses; if (!bus->self) - return NOTIFY_OK; + return; /* fixup bad _DCK function that rewrites * secondary bridge on slot @@ -143,11 +142,11 @@ static int post_dock_fixups(struct notifier_block *nb, unsigned long val, | ((unsigned int)(bus->busn_res.end) << 16); pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses); } - return NOTIFY_OK; } static const struct acpi_dock_ops acpiphp_dock_ops = { + .fixup = post_dock_fixups, .handler = hotplug_event_func, }; @@ -315,14 +314,6 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) &acpiphp_dock_ops, newfunc, acpiphp_dock_init, acpiphp_dock_release)) dbg("failed to register dock device\n"); - - /* we need to be notified when dock events happen - * outside of the hotplug operation, since we may - * need to do fixups before we can hotplug. - */ - newfunc->nb.notifier_call = post_dock_fixups; - if (register_dock_notifier(&newfunc->nb)) - dbg("failed to register a dock notifier"); } /* install notify handler */ @@ -472,7 +463,6 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) list_for_each_entry(func, &slot->funcs, sibling) { if (is_dock_device(func->handle)) { unregister_hotplug_dock_device(func->handle); - unregister_dock_notifier(&func->nb); } if (!(func->flags & FUNC_HAS_DCK)) { status = acpi_remove_notify_handler(func->handle, diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 0cf85786ed21..1cedfcb1bd88 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -113,14 +113,13 @@ void pci_acpi_crs_quirks(void); Dock Station -------------------------------------------------------------------------- */ struct acpi_dock_ops { + acpi_notify_handler fixup; acpi_notify_handler handler; acpi_notify_handler uevent; }; #ifdef CONFIG_ACPI_DOCK extern int is_dock_device(acpi_handle handle); -extern int register_dock_notifier(struct notifier_block *nb); -extern void unregister_dock_notifier(struct notifier_block *nb); extern int register_hotplug_dock_device(acpi_handle handle, const struct acpi_dock_ops *ops, void *context, @@ -132,13 +131,6 @@ static inline int is_dock_device(acpi_handle handle) { return 0; } -static inline int register_dock_notifier(struct notifier_block *nb) -{ - return -ENODEV; -} -static inline void unregister_dock_notifier(struct notifier_block *nb) -{ -} static inline int register_hotplug_dock_device(acpi_handle handle, const struct acpi_dock_ops *ops, void *context, -- cgit v1.2.3-71-gd317 From 728ce22b696f9f1404a74d7b2279a65933553a1b Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Wed, 12 Jun 2013 15:08:51 +0200 Subject: cpuidle: Make cpuidle's sysfs directory dynamically allocated The cpuidle sysfs code is designed to have a single instance of per CPU cpuidle directory. It is not possible to remove the sysfs entry and create it again. This is not a problem with the current code but future changes will add CPU hotplug support to enable/disable the device, so it will need to remove the sysfs entry like other subsystems do. That won't be possible without this change, because the kobj is a static object which can't be reused for kobj_init_and_add(). Add cpuidle_device_kobj to be allocated dynamically when adding/removing a sysfs entry which is consistent with the other cpuidle's sysfs entries. An added benefit is that the sysfs code is now more self-contained and the includes needed for sysfs can be moved from cpuidle.h directly into sysfs.c so as to reduce the total number of headers dragged along with cpuidle.h. [rjw: Changelog] Signed-off-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/sysfs.c | 63 +++++++++++++++++++++++++++++++++++++------------ include/linux/cpuidle.h | 7 +++--- 2 files changed, 51 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 7d4448a9fbbd..8739cc05228c 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include #include "cpuidle.h" @@ -167,14 +169,27 @@ struct cpuidle_attr { #define define_one_rw(_name, show, store) \ static struct cpuidle_attr attr_##_name = __ATTR(_name, 0644, show, store) -#define kobj_to_cpuidledev(k) container_of(k, struct cpuidle_device, kobj) #define attr_to_cpuidleattr(a) container_of(a, struct cpuidle_attr, attr) +struct cpuidle_device_kobj { + struct cpuidle_device *dev; + struct completion kobj_unregister; + struct kobject kobj; +}; + +static inline struct cpuidle_device *to_cpuidle_device(struct kobject *kobj) +{ + struct cpuidle_device_kobj *kdev = + container_of(kobj, struct cpuidle_device_kobj, kobj); + + return kdev->dev; +} + static ssize_t cpuidle_show(struct kobject *kobj, struct attribute *attr, char *buf) { int ret = -EIO; - struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); + struct cpuidle_device *dev = to_cpuidle_device(kobj); struct cpuidle_attr *cattr = attr_to_cpuidleattr(attr); if (cattr->show) { @@ -189,7 +204,7 @@ static ssize_t cpuidle_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { int ret = -EIO; - struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); + struct cpuidle_device *dev = to_cpuidle_device(kobj); struct cpuidle_attr *cattr = attr_to_cpuidleattr(attr); if (cattr->store) { @@ -207,9 +222,10 @@ static const struct sysfs_ops cpuidle_sysfs_ops = { static void cpuidle_sysfs_release(struct kobject *kobj) { - struct cpuidle_device *dev = kobj_to_cpuidledev(kobj); + struct cpuidle_device_kobj *kdev = + container_of(kobj, struct cpuidle_device_kobj, kobj); - complete(&dev->kobj_unregister); + complete(&kdev->kobj_unregister); } static struct kobj_type ktype_cpuidle = { @@ -377,6 +393,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device) { int i, ret = -ENOMEM; struct cpuidle_state_kobj *kobj; + struct cpuidle_device_kobj *kdev = device->kobj_dev; struct cpuidle_driver *drv = cpuidle_get_cpu_driver(device); /* state statistics */ @@ -389,7 +406,7 @@ static int cpuidle_add_state_sysfs(struct cpuidle_device *device) init_completion(&kobj->kobj_unregister); ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, - &device->kobj, "state%d", i); + &kdev->kobj, "state%d", i); if (ret) { kfree(kobj); goto error_state; @@ -506,6 +523,7 @@ static struct kobj_type ktype_driver_cpuidle = { static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev) { struct cpuidle_driver_kobj *kdrv; + struct cpuidle_device_kobj *kdev = dev->kobj_dev; struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); int ret; @@ -517,7 +535,7 @@ static int cpuidle_add_driver_sysfs(struct cpuidle_device *dev) init_completion(&kdrv->kobj_unregister); ret = kobject_init_and_add(&kdrv->kobj, &ktype_driver_cpuidle, - &dev->kobj, "driver"); + &kdev->kobj, "driver"); if (ret) { kfree(kdrv); return ret; @@ -586,16 +604,28 @@ void cpuidle_remove_device_sysfs(struct cpuidle_device *device) */ int cpuidle_add_sysfs(struct cpuidle_device *dev) { + struct cpuidle_device_kobj *kdev; struct device *cpu_dev = get_cpu_device((unsigned long)dev->cpu); int error; - init_completion(&dev->kobj_unregister); + kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); + if (!kdev) + return -ENOMEM; + kdev->dev = dev; + dev->kobj_dev = kdev; + + init_completion(&kdev->kobj_unregister); + + error = kobject_init_and_add(&kdev->kobj, &ktype_cpuidle, &cpu_dev->kobj, + "cpuidle"); + if (error) { + kfree(kdev); + return error; + } - error = kobject_init_and_add(&dev->kobj, &ktype_cpuidle, &cpu_dev->kobj, - "cpuidle"); - if (!error) - kobject_uevent(&dev->kobj, KOBJ_ADD); - return error; + kobject_uevent(&kdev->kobj, KOBJ_ADD); + + return 0; } /** @@ -604,6 +634,9 @@ int cpuidle_add_sysfs(struct cpuidle_device *dev) */ void cpuidle_remove_sysfs(struct cpuidle_device *dev) { - kobject_put(&dev->kobj); - wait_for_completion(&dev->kobj_unregister); + struct cpuidle_device_kobj *kdev = dev->kobj_dev; + + kobject_put(&kdev->kobj); + wait_for_completion(&kdev->kobj_unregister); + kfree(kdev); } diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 0bc4b74668e9..b922db53b7ab 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -13,8 +13,6 @@ #include #include -#include -#include #include #define CPUIDLE_STATE_MAX 10 @@ -61,6 +59,8 @@ struct cpuidle_state { #define CPUIDLE_DRIVER_FLAGS_MASK (0xFFFF0000) +struct cpuidle_device_kobj; + struct cpuidle_device { unsigned int registered:1; unsigned int enabled:1; @@ -71,9 +71,8 @@ struct cpuidle_device { struct cpuidle_state_usage states_usage[CPUIDLE_STATE_MAX]; struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX]; struct cpuidle_driver_kobj *kobj_driver; + struct cpuidle_device_kobj *kobj_dev; struct list_head device_list; - struct kobject kobj; - struct completion kobj_unregister; #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED int safe_state_index; -- cgit v1.2.3-71-gd317 From 1a7064380e6639e1cc5ed609ceb1ed46f3f188e3 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Wed, 12 Jun 2013 15:08:52 +0200 Subject: cpuidle: Add missing forward declarations of structures Add missing forward declarations of struct cpuidle_state_kobj and struct cpuidle_driver_kobj in cpuidle.h. [rjw: Changelog] Signed-off-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki --- include/linux/cpuidle.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index b922db53b7ab..781addc66f03 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -60,6 +60,8 @@ struct cpuidle_state { #define CPUIDLE_DRIVER_FLAGS_MASK (0xFFFF0000) struct cpuidle_device_kobj; +struct cpuidle_state_kobj; +struct cpuidle_driver_kobj; struct cpuidle_device { unsigned int registered:1; -- cgit v1.2.3-71-gd317 From 6e8b8726ad503214ba66e34aed69aff41de33489 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Jul 2013 11:19:32 +0930 Subject: PTR_RET is now PTR_ERR_OR_ZERO True, it's often used in return statements, but after much bikeshedding it's probably better to have an explicit name. (I tried just putting the IS_ERR check inside PTR_ERR itself and gcc usually generated no more code. But that clashes current expectations of how PTR_ERR behaves, so having a separate function is better). Suggested-by: Julia Lawall Suggested-by: "Michael S. Tsirkin" Cc: Julia Lawall Cc: "Michael S. Tsirkin" Signed-off-by: Rusty Russell --- include/linux/err.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/err.h b/include/linux/err.h index 221fcfb676c4..15f92e072450 100644 --- a/include/linux/err.h +++ b/include/linux/err.h @@ -52,7 +52,7 @@ static inline void * __must_check ERR_CAST(__force const void *ptr) return (void *) ptr; } -static inline int __must_check PTR_RET(__force const void *ptr) +static inline int __must_check PTR_ERR_OR_ZERO(__force const void *ptr) { if (IS_ERR(ptr)) return PTR_ERR(ptr); @@ -60,6 +60,9 @@ static inline int __must_check PTR_RET(__force const void *ptr) return 0; } +/* Deprecated */ +#define PTR_RET(p) PTR_ERR_OR_ZERO(p) + #endif #endif /* _LINUX_ERR_H */ -- cgit v1.2.3-71-gd317 From 326c29b6bedb7440b4232631686027af84a62cf5 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 4 Jul 2013 08:56:27 +0100 Subject: mfd: arizona: Add GPIO control register bit definitions Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- include/linux/mfd/arizona/gpio.h | 96 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 include/linux/mfd/arizona/gpio.h (limited to 'include') diff --git a/include/linux/mfd/arizona/gpio.h b/include/linux/mfd/arizona/gpio.h new file mode 100644 index 000000000000..d2146bb74f89 --- /dev/null +++ b/include/linux/mfd/arizona/gpio.h @@ -0,0 +1,96 @@ +/* + * GPIO configuration for Arizona devices + * + * Copyright 2013 Wolfson Microelectronics. PLC. + * + * Author: Charles Keepax + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _ARIZONA_GPIO_H +#define _ARIZONA_GPIO_H + +#define ARIZONA_GP_FN_TXLRCLK 0x00 +#define ARIZONA_GP_FN_GPIO 0x01 +#define ARIZONA_GP_FN_IRQ1 0x02 +#define ARIZONA_GP_FN_IRQ2 0x03 +#define ARIZONA_GP_FN_OPCLK 0x04 +#define ARIZONA_GP_FN_FLL1_OUT 0x05 +#define ARIZONA_GP_FN_FLL2_OUT 0x06 +#define ARIZONA_GP_FN_PWM1 0x08 +#define ARIZONA_GP_FN_PWM2 0x09 +#define ARIZONA_GP_FN_SYSCLK_UNDERCLOCKED 0x0A +#define ARIZONA_GP_FN_ASYNCCLK_UNDERCLOCKED 0x0B +#define ARIZONA_GP_FN_FLL1_LOCK 0x0C +#define ARIZONA_GP_FN_FLL2_LOCK 0x0D +#define ARIZONA_GP_FN_FLL1_CLOCK_OK 0x0F +#define ARIZONA_GP_FN_FLL2_CLOCK_OK 0x10 +#define ARIZONA_GP_FN_HEADPHONE_DET 0x12 +#define ARIZONA_GP_FN_MIC_DET 0x13 +#define ARIZONA_GP_FN_WSEQ_STATUS 0x15 +#define ARIZONA_GP_FN_CIF_ADDRESS_ERROR 0x16 +#define ARIZONA_GP_FN_ASRC1_LOCK 0x1A +#define ARIZONA_GP_FN_ASRC2_LOCK 0x1B +#define ARIZONA_GP_FN_ASRC_CONFIG_ERROR 0x1C +#define ARIZONA_GP_FN_DRC1_SIGNAL_DETECT 0x1D +#define ARIZONA_GP_FN_DRC1_ANTICLIP 0x1E +#define ARIZONA_GP_FN_DRC1_DECAY 0x1F +#define ARIZONA_GP_FN_DRC1_NOISE 0x20 +#define ARIZONA_GP_FN_DRC1_QUICK_RELEASE 0x21 +#define ARIZONA_GP_FN_DRC2_SIGNAL_DETECT 0x22 +#define ARIZONA_GP_FN_DRC2_ANTICLIP 0x23 +#define ARIZONA_GP_FN_DRC2_DECAY 0x24 +#define ARIZONA_GP_FN_DRC2_NOISE 0x25 +#define ARIZONA_GP_FN_DRC2_QUICK_RELEASE 0x26 +#define ARIZONA_GP_FN_MIXER_DROPPED_SAMPLE 0x27 +#define ARIZONA_GP_FN_AIF1_CONFIG_ERROR 0x28 +#define ARIZONA_GP_FN_AIF2_CONFIG_ERROR 0x29 +#define ARIZONA_GP_FN_AIF3_CONFIG_ERROR 0x2A +#define ARIZONA_GP_FN_SPK_TEMP_SHUTDOWN 0x2B +#define ARIZONA_GP_FN_SPK_TEMP_WARNING 0x2C +#define ARIZONA_GP_FN_UNDERCLOCKED 0x2D +#define ARIZONA_GP_FN_OVERCLOCKED 0x2E +#define ARIZONA_GP_FN_DSP_IRQ1 0x35 +#define ARIZONA_GP_FN_DSP_IRQ2 0x36 +#define ARIZONA_GP_FN_ASYNC_OPCLK 0x3D +#define ARIZONA_GP_FN_BOOT_DONE 0x44 +#define ARIZONA_GP_FN_DSP1_RAM_READY 0x45 +#define ARIZONA_GP_FN_SYSCLK_ENA_STATUS 0x4B +#define ARIZONA_GP_FN_ASYNCCLK_ENA_STATUS 0x4C + +#define ARIZONA_GPN_DIR 0x8000 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_MASK 0x8000 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_SHIFT 15 /* GPN_DIR */ +#define ARIZONA_GPN_DIR_WIDTH 1 /* GPN_DIR */ +#define ARIZONA_GPN_PU 0x4000 /* GPN_PU */ +#define ARIZONA_GPN_PU_MASK 0x4000 /* GPN_PU */ +#define ARIZONA_GPN_PU_SHIFT 14 /* GPN_PU */ +#define ARIZONA_GPN_PU_WIDTH 1 /* GPN_PU */ +#define ARIZONA_GPN_PD 0x2000 /* GPN_PD */ +#define ARIZONA_GPN_PD_MASK 0x2000 /* GPN_PD */ +#define ARIZONA_GPN_PD_SHIFT 13 /* GPN_PD */ +#define ARIZONA_GPN_PD_WIDTH 1 /* GPN_PD */ +#define ARIZONA_GPN_LVL 0x0800 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_MASK 0x0800 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_SHIFT 11 /* GPN_LVL */ +#define ARIZONA_GPN_LVL_WIDTH 1 /* GPN_LVL */ +#define ARIZONA_GPN_POL 0x0400 /* GPN_POL */ +#define ARIZONA_GPN_POL_MASK 0x0400 /* GPN_POL */ +#define ARIZONA_GPN_POL_SHIFT 10 /* GPN_POL */ +#define ARIZONA_GPN_POL_WIDTH 1 /* GPN_POL */ +#define ARIZONA_GPN_OP_CFG 0x0200 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_MASK 0x0200 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_SHIFT 9 /* GPN_OP_CFG */ +#define ARIZONA_GPN_OP_CFG_WIDTH 1 /* GPN_OP_CFG */ +#define ARIZONA_GPN_DB 0x0100 /* GPN_DB */ +#define ARIZONA_GPN_DB_MASK 0x0100 /* GPN_DB */ +#define ARIZONA_GPN_DB_SHIFT 8 /* GPN_DB */ +#define ARIZONA_GPN_DB_WIDTH 1 /* GPN_DB */ +#define ARIZONA_GPN_FN_MASK 0x007F /* GPN_DB */ +#define ARIZONA_GPN_FN_SHIFT 0 /* GPN_DB */ +#define ARIZONA_GPN_FN_WIDTH 7 /* GPN_DB */ + +#endif -- cgit v1.2.3-71-gd317 From 10175b3b2fdd287515db4c103d96b323bb4cd690 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 3 Jul 2013 16:37:59 +0800 Subject: ARM: atmel-ssc: change phybase type to dma_addr_t as the phybase paramter only used for DMA operation, change it's type from resource_size_t to dma_addr_t Signed-off-by: Bo Shen Reviewed-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/linux/atmel-ssc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/atmel-ssc.h b/include/linux/atmel-ssc.h index deb0ae58b99b..66a0e5384edd 100644 --- a/include/linux/atmel-ssc.h +++ b/include/linux/atmel-ssc.h @@ -11,7 +11,7 @@ struct atmel_ssc_platform_data { struct ssc_device { struct list_head list; - resource_size_t phybase; + dma_addr_t phybase; void __iomem *regs; struct platform_device *pdev; struct atmel_ssc_platform_data *pdata; -- cgit v1.2.3-71-gd317 From dc9ceed6a12aff627c81e01ada191e8a23fcbe3e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 4 Jul 2013 17:27:14 +0100 Subject: regulator: core: Make set_voltage_tol() try for mid-range first The expected semantic for something expressed as a tolerance is that it should deliver the specified value with some deviation allowed but this is not what set_voltage_tol() currently does. Instead it just passes the maximum possible range to set_voltage() which will typically result in a voltage aimed at lower than the target voltage. Instead first try to set a voltage between the target voltage and the upper limit, then fall back on the full range. This will be much more robust against physical variation in systems and makes the API behave more like users would expect. Signed-off-by: Mark Brown --- include/linux/regulator/consumer.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 3a76389c6aaa..3610df8dd229 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -369,8 +369,11 @@ static inline int regulator_count_voltages(struct regulator *regulator) static inline int regulator_set_voltage_tol(struct regulator *regulator, int new_uV, int tol_uV) { - return regulator_set_voltage(regulator, - new_uV - tol_uV, new_uV + tol_uV); + if (regulator_set_voltage(regulator, new_uV, new_uV + tol_uV) == 0) + return 0; + else + return regulator_set_voltage(regulator, + new_uV - tol_uV, new_uV + tol_uV); } static inline int regulator_is_supported_voltage_tol(struct regulator *regulator, -- cgit v1.2.3-71-gd317 From 94d33c02c7186b69849c292e1216a08ad1c0d99d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 2 Jul 2013 22:52:41 +0100 Subject: regulator: core: Add helpers for multiple linear ranges Many regulators have several linear ranges of selector with different step sizes, for example offering better resolution at lower voltages. Provide regulator_{map,list}_voltage_linear_range() allowing these regulators to use generic code. To do so a table of regulator_linear_range structs needs to be pointed to from the descriptor. This was inspired by similar code included in a driver submission from Chao Xie and Yi Zhang at Marvell. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 87 ++++++++++++++++++++++++++++++++++++++++ include/linux/regulator/driver.h | 25 ++++++++++++ 2 files changed, 112 insertions(+) (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 288c75abc190..e8604be4c66d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2078,6 +2078,43 @@ int regulator_list_voltage_linear(struct regulator_dev *rdev, } EXPORT_SYMBOL_GPL(regulator_list_voltage_linear); +/** + * regulator_list_voltage_linear_range - List voltages for linear ranges + * + * @rdev: Regulator device + * @selector: Selector to convert into a voltage + * + * Regulators with a series of simple linear mappings between voltages + * and selectors can set linear_ranges in the regulator descriptor and + * then use this function as their list_voltage() operation, + */ +int regulator_list_voltage_linear_range(struct regulator_dev *rdev, + unsigned int selector) +{ + const struct regulator_linear_range *range; + int i; + + if (!rdev->desc->n_linear_ranges) { + BUG_ON(!rdev->desc->n_linear_ranges); + return -EINVAL; + } + + for (i = 0; i < rdev->desc->n_linear_ranges; i++) { + range = &rdev->desc->linear_ranges[i]; + + if (!(selector >= range->min_sel && + selector <= range->max_sel)) + continue; + + selector -= range->min_sel; + + return range->min_uV + (range->uV_step * selector); + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(regulator_list_voltage_linear_range); + /** * regulator_list_voltage_table - List voltages with table based mapping * @@ -2368,6 +2405,56 @@ int regulator_map_voltage_linear(struct regulator_dev *rdev, } EXPORT_SYMBOL_GPL(regulator_map_voltage_linear); +/** + * regulator_map_voltage_linear - map_voltage() for multiple linear ranges + * + * @rdev: Regulator to operate on + * @min_uV: Lower bound for voltage + * @max_uV: Upper bound for voltage + * + * Drivers providing linear_ranges in their descriptor can use this as + * their map_voltage() callback. + */ +int regulator_map_voltage_linear_range(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + const struct regulator_linear_range *range; + int ret = -EINVAL; + int voltage, i; + + if (!rdev->desc->n_linear_ranges) { + BUG_ON(!rdev->desc->n_linear_ranges); + return -EINVAL; + } + + for (i = 0; i < rdev->desc->n_linear_ranges; i++) { + range = &rdev->desc->linear_ranges[i]; + + if (!(min_uV <= range->max_uV && max_uV >= range->min_uV)) + continue; + + if (min_uV <= range->min_uV) + min_uV = range->min_uV; + + ret = DIV_ROUND_UP(min_uV - range->min_uV, range->uV_step); + if (ret < 0) + return ret; + + break; + } + + if (i == rdev->desc->n_linear_ranges) + return -EINVAL; + + /* Map back into a voltage to verify we're still in bounds */ + voltage = rdev->desc->ops->list_voltage(rdev, ret); + if (voltage < min_uV || voltage > max_uV) + return -EINVAL; + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_map_voltage_linear_range); + static int _regulator_do_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) { diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 6700cc94bdd1..67e13aa5a478 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -39,6 +39,24 @@ enum regulator_status { REGULATOR_STATUS_UNDEFINED, }; +/** + * Specify a range of voltages for regulator_map_linar_range() and + * regulator_list_linear_range(). + * + * @min_uV: Lowest voltage in range + * @max_uV: Highest voltage in range + * @min_sel: Lowest selector for range + * @max_sel: Highest selector for range + * @uV_step: Step size + */ +struct regulator_linear_range { + unsigned int min_uV; + unsigned int max_uV; + unsigned int min_sel; + unsigned int max_sel; + unsigned int uV_step; +}; + /** * struct regulator_ops - regulator operations. * @@ -223,6 +241,9 @@ struct regulator_desc { unsigned int linear_min_sel; unsigned int ramp_delay; + const struct regulator_linear_range *linear_ranges; + int n_linear_ranges; + const unsigned int *volt_table; unsigned int vsel_reg; @@ -326,10 +347,14 @@ int regulator_mode_to_status(unsigned int); int regulator_list_voltage_linear(struct regulator_dev *rdev, unsigned int selector); +int regulator_list_voltage_linear_range(struct regulator_dev *rdev, + unsigned int selector); int regulator_list_voltage_table(struct regulator_dev *rdev, unsigned int selector); int regulator_map_voltage_linear(struct regulator_dev *rdev, int min_uV, int max_uV); +int regulator_map_voltage_linear_range(struct regulator_dev *rdev, + int min_uV, int max_uV); int regulator_map_voltage_iterate(struct regulator_dev *rdev, int min_uV, int max_uV); int regulator_map_voltage_ascend(struct regulator_dev *rdev, -- cgit v1.2.3-71-gd317 From 1653ccf4c52df6a4abe8ec2f33f2cb2896d129ea Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Sat, 29 Jun 2013 18:21:15 +0530 Subject: regulator: core: Add support for disabling ramp delay Some hardwares support disabling ramp delay, so adding ramp_disable flag to constraints. It will be used to figure out whether ramp_delay in constraints is explicitly set to zero or its unintialized (zero by default). And we don't need to call set_voltage_time_sel() for regulators for whom ramp delay is disabled in constraints. Signed-off-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/regulator.txt | 2 ++ drivers/regulator/core.c | 6 ++++-- drivers/regulator/of_regulator.c | 12 +++++++++--- include/linux/regulator/machine.h | 1 + 4 files changed, 16 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index 48a3b8e5d6bd..2bd8f0978765 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -12,6 +12,8 @@ Optional properties: - regulator-allow-bypass: allow the regulator to go into bypass mode - -supply: phandle to the parent supply/regulator node - regulator-ramp-delay: ramp delay for regulator(in uV/uS) + For hardwares which support disabling ramp rate, it should be explicitly + intialised to zero (regulator-ramp-delay = <0>) for disabling ramp delay. Deprecated properties: - regulator-compatible: If a regulator chip contains multiple diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 288c75abc190..6e6371c2346c 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -984,7 +984,8 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } - if (rdev->constraints->ramp_delay && ops->set_ramp_delay) { + if ((rdev->constraints->ramp_delay || rdev->constraints->ramp_disable) + && ops->set_ramp_delay) { ret = ops->set_ramp_delay(rdev, rdev->constraints->ramp_delay); if (ret < 0) { rdev_err(rdev, "failed to set ramp_delay\n"); @@ -2438,7 +2439,8 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, } /* Call set_voltage_time_sel if successfully obtained old_selector */ - if (ret == 0 && _regulator_is_enabled(rdev) && old_selector >= 0 && + if (ret == 0 && !rdev->constraints->ramp_disable && + _regulator_is_enabled(rdev) && old_selector >= 0 && old_selector != selector && rdev->desc->ops->set_voltage_time_sel) { delay = rdev->desc->ops->set_voltage_time_sel(rdev, diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index f3c8f8f9dc39..7827384680d6 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -21,6 +21,7 @@ static void of_get_regulation_constraints(struct device_node *np, { const __be32 *min_uV, *max_uV, *uV_offset; const __be32 *min_uA, *max_uA, *ramp_delay; + struct property *prop; struct regulation_constraints *constraints = &(*init_data)->constraints; constraints->name = of_get_property(np, "regulator-name", NULL); @@ -64,9 +65,14 @@ static void of_get_regulation_constraints(struct device_node *np, if (of_property_read_bool(np, "regulator-allow-bypass")) constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS; - ramp_delay = of_get_property(np, "regulator-ramp-delay", NULL); - if (ramp_delay) - constraints->ramp_delay = be32_to_cpu(*ramp_delay); + prop = of_find_property(np, "regulator-ramp-delay", NULL); + if (prop && prop->value) { + ramp_delay = prop->value; + if (*ramp_delay) + constraints->ramp_delay = be32_to_cpu(*ramp_delay); + else + constraints->ramp_disable = true; + } } /** diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 36adbc82de6a..999b20ce06cf 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -134,6 +134,7 @@ struct regulation_constraints { unsigned always_on:1; /* regulator never off when system is on */ unsigned boot_on:1; /* bootloader/firmware enabled regulator */ unsigned apply_uV:1; /* apply uV constraint if min == max */ + unsigned ramp_disable:1; /* disable ramp delay */ }; /** -- cgit v1.2.3-71-gd317 From 874b31585650afa6745de5133849365e7e6af418 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 5 Jul 2013 11:44:49 +0100 Subject: spi/bitbang: Unexport spi_bitbang_transfer() Currently no drivers use the ability to override spi_bitbang_transfer() and if any started this would make it harder to convert the bitbang code to use transfer_one_message() so remove the export in order to prevent anyone starting. Signed-off-by: Mark Brown --- drivers/spi/spi-bitbang.c | 3 +-- include/linux/spi/spi_bitbang.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index a63d7da3bfe2..495ce0a51d2f 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -376,7 +376,7 @@ static void bitbang_work(struct work_struct *work) /** * spi_bitbang_transfer - default submit to transfer queue */ -int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m) +static int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m) { struct spi_bitbang *bitbang; unsigned long flags; @@ -398,7 +398,6 @@ int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m) return status; } -EXPORT_SYMBOL_GPL(spi_bitbang_transfer); /*----------------------------------------------------------------------*/ diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h index f987a2bee16a..b5aa215493f6 100644 --- a/include/linux/spi/spi_bitbang.h +++ b/include/linux/spi/spi_bitbang.h @@ -41,7 +41,6 @@ struct spi_bitbang { */ extern int spi_bitbang_setup(struct spi_device *spi); extern void spi_bitbang_cleanup(struct spi_device *spi); -extern int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m); extern int spi_bitbang_setup_transfer(struct spi_device *spi, struct spi_transfer *t); -- cgit v1.2.3-71-gd317 From a2fd4f9fa3b9f051550b36c4dfa74bc32bda24ee Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 10 Jul 2013 14:57:26 +0100 Subject: spi: Support transfer speed checking in the core Allow drivers to avoid implementing their own checks for simple rates by specifying the limits in the master structure. Signed-off-by: Mark Brown --- drivers/spi/spi.c | 7 +++++++ include/linux/spi/spi.h | 6 ++++++ 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 978dda2c5239..a52f16685d6a 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1387,6 +1387,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) BIT(xfer->bits_per_word - 1))) return -EINVAL; } + + if (xfer->speed_hz && master->min_speed_hz && + xfer->speed_hz < master->min_speed_hz) + return -EINVAL; + if (xfer->speed_hz && master->max_speed_hz && + xfer->speed_hz > master->max_speed_hz) + return -EINVAL; } message->spi = spi; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 28e440be1c07..cdf668156154 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -233,6 +233,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * suported. If set, the SPI core will reject any transfer with an * unsupported bits_per_word. If not set, this value is simply ignored, * and it's up to the individual driver to perform any validation. + * @min_speed_hz: Lowest supported transfer speed + * @max_speed_hz: Highest supported transfer speed * @flags: other constraints relevant to this driver * @bus_lock_spinlock: spinlock for SPI bus locking * @bus_lock_mutex: mutex for SPI bus locking @@ -312,6 +314,10 @@ struct spi_master { #define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0UL : (BIT(bits) - 1)) #define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1)) + /* limits on transfer speed */ + u32 min_speed_hz; + u32 max_speed_hz; + /* other constraints relevant to this driver */ u16 flags; #define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */ -- cgit v1.2.3-71-gd317 From 1696d9dc57e062ce5200f6a42a6aaada15b434bb Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Mon, 15 Jul 2013 10:15:09 +0200 Subject: ACPI: Remove the old /proc/acpi/event interface It is quite some time that this one has been deprecated. Get rid of it. Should some really important user be overseen, it may be reverted and the userspace program worked on first, but it is time to do something to get rid of this old stuff... Signed-off-by: Thomas Renninger Acked-by: Matthew Garrett Acked-by: Henrique de Moraes Holschuh Signed-off-by: Rafael J. Wysocki --- Documentation/laptops/asus-laptop.txt | 8 +-- Documentation/laptops/sony-laptop.txt | 8 +-- drivers/acpi/Kconfig | 18 ------ drivers/acpi/ac.c | 1 - drivers/acpi/acpi_pad.c | 1 - drivers/acpi/battery.c | 2 - drivers/acpi/bus.c | 98 ----------------------------- drivers/acpi/button.c | 2 - drivers/acpi/event.c | 106 -------------------------------- drivers/acpi/processor_driver.c | 4 -- drivers/acpi/sbs.c | 15 +---- drivers/acpi/thermal.c | 3 - drivers/acpi/video.c | 10 --- drivers/char/sonypi.c | 5 -- drivers/pci/hotplug/acpiphp_ibm.c | 1 - drivers/platform/x86/asus-laptop.c | 1 - drivers/platform/x86/eeepc-laptop.c | 1 - drivers/platform/x86/fujitsu-laptop.c | 4 -- drivers/platform/x86/panasonic-laptop.c | 3 - drivers/platform/x86/sony-laptop.c | 4 -- drivers/platform/x86/thinkpad_acpi.c | 11 ---- include/acpi/acpi_bus.h | 9 --- 22 files changed, 10 insertions(+), 305 deletions(-) (limited to 'include') diff --git a/Documentation/laptops/asus-laptop.txt b/Documentation/laptops/asus-laptop.txt index 69f9fb3701e0..79a1bc675a8d 100644 --- a/Documentation/laptops/asus-laptop.txt +++ b/Documentation/laptops/asus-laptop.txt @@ -8,8 +8,8 @@ http://acpi4asus.sf.net/ This driver provides support for extra features of ACPI-compatible ASUS laptops. It may also support some MEDION, JVC or VICTOR laptops (such as MEDION 9675 or - VICTOR XP7210 for example). It makes all the extra buttons generate standard - ACPI events that go through /proc/acpi/events and input events (like keyboards). + VICTOR XP7210 for example). It makes all the extra buttons generate input + events (like keyboards). On some models adds support for changing the display brightness and output, switching the LCD backlight on and off, and most importantly, allows you to blink those fancy LEDs intended for reporting mail and wireless status. @@ -55,8 +55,8 @@ Usage DSDT) to me. That's all, now, all the events generated by the hotkeys of your laptop - should be reported in your /proc/acpi/event entry. You can check with - "acpi_listen". + should be reported via netlink events. You can check with + "acpi_genl monitor" (part of the acpica project). Hotkeys are also reported as input keys (like keyboards) you can check which key are supported using "xev" under X11. diff --git a/Documentation/laptops/sony-laptop.txt b/Documentation/laptops/sony-laptop.txt index 0d5ac7f5287e..978b1e615155 100644 --- a/Documentation/laptops/sony-laptop.txt +++ b/Documentation/laptops/sony-laptop.txt @@ -12,10 +12,10 @@ Fn keys (hotkeys): ------------------ Some models report hotkeys through the SNC or SPIC devices, such events are reported both through the ACPI subsystem as acpi events and through the INPUT -subsystem. See the logs of acpid or /proc/acpi/event and -/proc/bus/input/devices to find out what those events are and which input -devices are created by the driver. Additionally, loading the driver with the -debug option will report all events in the kernel log. +subsystem. See the logs of /proc/bus/input/devices to find out what those +events are and which input devices are created by the driver. +Additionally, loading the driver with the debug option will report all events +in the kernel log. The "scancodes" passed to the input system (that can be remapped with udev) are indexes to the table "sony_laptop_input_keycode_map" in the sony-laptop.c diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 100bd724f648..3278a210c435 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -91,24 +91,6 @@ config ACPI_EC_DEBUGFS Thus this option is a debug option that helps to write ACPI drivers and can be used to identify ACPI code or EC firmware bugs. -config ACPI_PROC_EVENT - bool "Deprecated /proc/acpi/event support" - depends on PROC_FS - default y - help - A user-space daemon, acpid, typically reads /proc/acpi/event - and handles all ACPI-generated events. - - These events are now delivered to user-space either - via the input layer or as netlink events. - - This build option enables the old code for legacy - user-space implementation. After some time, this will - be moved under CONFIG_ACPI_PROCFS, and then deleted. - - Say Y here to retain the old behaviour. Say N if your - user-space is newer than kernel 2.6.23 (September 2007). - config ACPI_AC tristate "AC Adapter" depends on X86 diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 4f4e741d34b2..f37beaa32750 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -267,7 +267,6 @@ static void acpi_ac_notify(struct acpi_device *device, u32 event) msleep(ac_sleep_before_get_state_ms); acpi_ac_get_state(ac); - acpi_bus_generate_proc_event(device, event, (u32) ac->state); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, (u32) ac->state); diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index 27bb6a91de5f..6230637054c6 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -452,7 +452,6 @@ static void acpi_pad_notify(acpi_handle handle, u32 event, switch (event) { case ACPI_PROCESSOR_AGGREGATOR_NOTIFY: acpi_pad_handle_notify(handle); - acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, 0); break; diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 082b4dd252a8..33dfa851899d 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1034,8 +1034,6 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event) if (event == ACPI_BATTERY_NOTIFY_INFO) acpi_battery_refresh(battery); acpi_battery_update(battery); - acpi_bus_generate_proc_event(device, event, - acpi_battery_present(battery)); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, acpi_battery_present(battery)); diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index a5bb33bab448..b6e9a3786e2d 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -345,104 +345,6 @@ static void acpi_bus_osc_support(void) /* do we need to check other returned cap? Sounds no */ } -/* -------------------------------------------------------------------------- - Event Management - -------------------------------------------------------------------------- */ - -#ifdef CONFIG_ACPI_PROC_EVENT -static DEFINE_SPINLOCK(acpi_bus_event_lock); - -LIST_HEAD(acpi_bus_event_list); -DECLARE_WAIT_QUEUE_HEAD(acpi_bus_event_queue); - -extern int event_is_open; - -int acpi_bus_generate_proc_event4(const char *device_class, const char *bus_id, u8 type, int data) -{ - struct acpi_bus_event *event; - unsigned long flags; - - /* drop event on the floor if no one's listening */ - if (!event_is_open) - return 0; - - event = kzalloc(sizeof(struct acpi_bus_event), GFP_ATOMIC); - if (!event) - return -ENOMEM; - - strcpy(event->device_class, device_class); - strcpy(event->bus_id, bus_id); - event->type = type; - event->data = data; - - spin_lock_irqsave(&acpi_bus_event_lock, flags); - list_add_tail(&event->node, &acpi_bus_event_list); - spin_unlock_irqrestore(&acpi_bus_event_lock, flags); - - wake_up_interruptible(&acpi_bus_event_queue); - - return 0; - -} - -EXPORT_SYMBOL_GPL(acpi_bus_generate_proc_event4); - -int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data) -{ - if (!device) - return -EINVAL; - return acpi_bus_generate_proc_event4(device->pnp.device_class, - device->pnp.bus_id, type, data); -} - -EXPORT_SYMBOL(acpi_bus_generate_proc_event); - -int acpi_bus_receive_event(struct acpi_bus_event *event) -{ - unsigned long flags; - struct acpi_bus_event *entry = NULL; - - DECLARE_WAITQUEUE(wait, current); - - - if (!event) - return -EINVAL; - - if (list_empty(&acpi_bus_event_list)) { - - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&acpi_bus_event_queue, &wait); - - if (list_empty(&acpi_bus_event_list)) - schedule(); - - remove_wait_queue(&acpi_bus_event_queue, &wait); - set_current_state(TASK_RUNNING); - - if (signal_pending(current)) - return -ERESTARTSYS; - } - - spin_lock_irqsave(&acpi_bus_event_lock, flags); - if (!list_empty(&acpi_bus_event_list)) { - entry = list_entry(acpi_bus_event_list.next, - struct acpi_bus_event, node); - list_del(&entry->node); - } - spin_unlock_irqrestore(&acpi_bus_event_lock, flags); - - if (!entry) - return -ENODEV; - - memcpy(event, entry, sizeof(struct acpi_bus_event)); - - kfree(entry); - - return 0; -} - -#endif /* CONFIG_ACPI_PROC_EVENT */ - /* -------------------------------------------------------------------------- Notification Handling -------------------------------------------------------------------------- */ diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index d2e617b5b3f6..a55773801c5f 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -303,8 +303,6 @@ static void acpi_button_notify(struct acpi_device *device, u32 event) pm_wakeup_event(&device->dev, 0); } - - acpi_bus_generate_proc_event(device, event, ++button->pushed); break; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c index 1442737cedec..8247fcdde079 100644 --- a/drivers/acpi/event.c +++ b/drivers/acpi/event.c @@ -21,100 +21,6 @@ #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME("event"); -#ifdef CONFIG_ACPI_PROC_EVENT -/* Global vars for handling event proc entry */ -static DEFINE_SPINLOCK(acpi_system_event_lock); -int event_is_open = 0; -extern struct list_head acpi_bus_event_list; -extern wait_queue_head_t acpi_bus_event_queue; - -static int acpi_system_open_event(struct inode *inode, struct file *file) -{ - spin_lock_irq(&acpi_system_event_lock); - - if (event_is_open) - goto out_busy; - - event_is_open = 1; - - spin_unlock_irq(&acpi_system_event_lock); - return 0; - - out_busy: - spin_unlock_irq(&acpi_system_event_lock); - return -EBUSY; -} - -static ssize_t -acpi_system_read_event(struct file *file, char __user * buffer, size_t count, - loff_t * ppos) -{ - int result = 0; - struct acpi_bus_event event; - static char str[ACPI_MAX_STRING]; - static int chars_remaining = 0; - static char *ptr; - - if (!chars_remaining) { - memset(&event, 0, sizeof(struct acpi_bus_event)); - - if ((file->f_flags & O_NONBLOCK) - && (list_empty(&acpi_bus_event_list))) - return -EAGAIN; - - result = acpi_bus_receive_event(&event); - if (result) - return result; - - chars_remaining = sprintf(str, "%s %s %08x %08x\n", - event.device_class ? event. - device_class : "", - event.bus_id ? event. - bus_id : "", event.type, - event.data); - ptr = str; - } - - if (chars_remaining < count) { - count = chars_remaining; - } - - if (copy_to_user(buffer, ptr, count)) - return -EFAULT; - - *ppos += count; - chars_remaining -= count; - ptr += count; - - return count; -} - -static int acpi_system_close_event(struct inode *inode, struct file *file) -{ - spin_lock_irq(&acpi_system_event_lock); - event_is_open = 0; - spin_unlock_irq(&acpi_system_event_lock); - return 0; -} - -static unsigned int acpi_system_poll_event(struct file *file, poll_table * wait) -{ - poll_wait(file, &acpi_bus_event_queue, wait); - if (!list_empty(&acpi_bus_event_list)) - return POLLIN | POLLRDNORM; - return 0; -} - -static const struct file_operations acpi_system_event_ops = { - .owner = THIS_MODULE, - .open = acpi_system_open_event, - .read = acpi_system_read_event, - .release = acpi_system_close_event, - .poll = acpi_system_poll_event, - .llseek = default_llseek, -}; -#endif /* CONFIG_ACPI_PROC_EVENT */ - /* ACPI notifier chain */ static BLOCKING_NOTIFIER_HEAD(acpi_chain_head); @@ -280,9 +186,6 @@ static int acpi_event_genetlink_init(void) static int __init acpi_event_init(void) { -#ifdef CONFIG_ACPI_PROC_EVENT - struct proc_dir_entry *entry; -#endif int error = 0; if (acpi_disabled) @@ -293,15 +196,6 @@ static int __init acpi_event_init(void) if (error) printk(KERN_WARNING PREFIX "Failed to create genetlink family for ACPI event\n"); - -#ifdef CONFIG_ACPI_PROC_EVENT - /* 'event' [R] */ - entry = proc_create("event", S_IRUSR, acpi_root_dir, - &acpi_system_event_ops); - if (!entry) - return -ENODEV; -#endif - return 0; } diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 823be116619e..94b7b3b5559e 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -91,21 +91,17 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) acpi_processor_ppc_has_changed(pr, 1); if (saved == pr->performance_platform_limit) break; - acpi_bus_generate_proc_event(device, event, - pr->performance_platform_limit); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, pr->performance_platform_limit); break; case ACPI_PROCESSOR_NOTIFY_POWER: acpi_processor_cst_has_changed(pr); - acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, 0); break; case ACPI_PROCESSOR_NOTIFY_THROTTLING: acpi_processor_tstate_has_changed(pr); - acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, 0); break; diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index b6241eeb1132..aef7e1cd1e5d 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -873,14 +873,9 @@ static void acpi_sbs_callback(void *context) u8 saved_charger_state = sbs->charger_present; u8 saved_battery_state; acpi_ac_get_present(sbs); - if (sbs->charger_present != saved_charger_state) { -#ifdef CONFIG_ACPI_PROC_EVENT - acpi_bus_generate_proc_event4(ACPI_AC_CLASS, ACPI_AC_DIR_NAME, - ACPI_SBS_NOTIFY_STATUS, - sbs->charger_present); -#endif + if (sbs->charger_present != saved_charger_state) kobject_uevent(&sbs->charger.dev->kobj, KOBJ_CHANGE); - } + if (sbs->manager_present) { for (id = 0; id < MAX_SBS_BAT; ++id) { if (!(sbs->batteries_supported & (1 << id))) @@ -890,12 +885,6 @@ static void acpi_sbs_callback(void *context) acpi_battery_read(bat); if (saved_battery_state == bat->present) continue; -#ifdef CONFIG_ACPI_PROC_EVENT - acpi_bus_generate_proc_event4(ACPI_BATTERY_CLASS, - bat->name, - ACPI_SBS_NOTIFY_STATUS, - bat->present); -#endif kobject_uevent(&bat->bat.dev->kobj, KOBJ_CHANGE); } } diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index a33821ca3895..547a906a7662 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -769,7 +769,6 @@ static int thermal_notify(struct thermal_zone_device *thermal, int trip, else return 0; - acpi_bus_generate_proc_event(tz->device, type, 1); acpi_bus_generate_netlink_event(tz->device->pnp.device_class, dev_name(&tz->device->dev), type, 1); @@ -980,14 +979,12 @@ static void acpi_thermal_notify(struct acpi_device *device, u32 event) case ACPI_THERMAL_NOTIFY_THRESHOLDS: acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS); acpi_thermal_check(tz); - acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, 0); break; case ACPI_THERMAL_NOTIFY_DEVICES: acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES); acpi_thermal_check(tz); - acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, 0); break; diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 5d7075d25700..8f91a3783191 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1556,7 +1556,6 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event) switch (event) { case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch, * most likely via hotkey. */ - acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_SWITCHVIDEOMODE; break; @@ -1564,20 +1563,16 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event) * connector. */ acpi_video_device_enumerate(video); acpi_video_device_rebind(video); - acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_SWITCHVIDEOMODE; break; case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */ - acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_SWITCHVIDEOMODE; break; case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */ - acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_VIDEO_NEXT; break; case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */ - acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_VIDEO_PREV; break; @@ -1620,31 +1615,26 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */ if (brightness_switch_enabled) acpi_video_switch_brightness(video_device, event); - acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_BRIGHTNESS_CYCLE; break; case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */ if (brightness_switch_enabled) acpi_video_switch_brightness(video_device, event); - acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_BRIGHTNESSUP; break; case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */ if (brightness_switch_enabled) acpi_video_switch_brightness(video_device, event); - acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_BRIGHTNESSDOWN; break; case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightness */ if (brightness_switch_enabled) acpi_video_switch_brightness(video_device, event); - acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_BRIGHTNESS_ZERO; break; case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */ if (brightness_switch_enabled) acpi_video_switch_brightness(video_device, event); - acpi_bus_generate_proc_event(device, event, 0); keycode = KEY_DISPLAY_OFF; break; default: diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index bf2349dbbf7f..7cc1fe2241fd 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -876,11 +876,6 @@ found: if (useinput) sonypi_report_input_event(event); -#ifdef CONFIG_ACPI - if (sonypi_acpi_device) - acpi_bus_generate_proc_event(sonypi_acpi_device, 1, event); -#endif - kfifo_in_locked(&sonypi_device.fifo, (unsigned char *)&event, sizeof(event), &sonypi_device.fifo_lock); kill_fasync(&sonypi_device.fifo_async, SIGIO, POLL_IN); diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index c35e8ad6db01..5394fffdf167 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -270,7 +270,6 @@ static void ibm_handle_events(acpi_handle handle, u32 event, void *context) if (subevent == 0x80) { dbg("%s: generationg bus event\n", __func__); - acpi_bus_generate_proc_event(note->device, note->event, detail); acpi_bus_generate_netlink_event(note->device->pnp.device_class, dev_name(¬e->device->dev), note->event, detail); diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 8e268da6fdbd..0e9c169b42f8 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1543,7 +1543,6 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event) /* TODO Find a better way to handle events count. */ count = asus->event_count[event % 128]++; - acpi_bus_generate_proc_event(asus->device, event, count); acpi_bus_generate_netlink_event(asus->device->pnp.device_class, dev_name(&asus->device->dev), event, count); diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 5d26e70bed6c..a6afd4108beb 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1269,7 +1269,6 @@ static void eeepc_acpi_notify(struct acpi_device *device, u32 event) if (event > ACPI_MAX_SYS_NOTIFY) return; count = eeepc->event_count[event % 128]++; - acpi_bus_generate_proc_event(device, event, count); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), event, count); diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 1c9386e7c58c..52b8a97efde1 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -773,8 +773,6 @@ static void acpi_fujitsu_notify(struct acpi_device *device, u32 event) else set_lcd_level(newb); } - acpi_bus_generate_proc_event(fujitsu->dev, - ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0); keycode = KEY_BRIGHTNESSUP; } else if (oldb > newb) { if (disable_brightness_adjust != 1) { @@ -783,8 +781,6 @@ static void acpi_fujitsu_notify(struct acpi_device *device, u32 event) else set_lcd_level(newb); } - acpi_bus_generate_proc_event(fujitsu->dev, - ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0); keycode = KEY_BRIGHTNESSDOWN; } break; diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 4add9a31bf60..984253da365d 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -464,9 +464,6 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) "error getting hotkey status\n")); return; } - - acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result); - if (!sparse_keymap_report_event(hotk_input_dev, result & 0xf, result & 0x80, false)) ACPI_DEBUG_PRINT((ACPI_DB_ERROR, diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 2ac045f27f10..069821b1fc22 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1275,9 +1275,6 @@ static void sony_nc_notify(struct acpi_device *device, u32 event) ev_type = HOTKEY; sony_laptop_report_input_event(real_ev); } - - acpi_bus_generate_proc_event(sony_nc_acpi_device, ev_type, real_ev); - acpi_bus_generate_netlink_event(sony_nc_acpi_device->pnp.device_class, dev_name(&sony_nc_acpi_device->dev), ev_type, real_ev); } @@ -4243,7 +4240,6 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id) found: sony_laptop_report_input_event(device_event); - acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event); sonypi_compat_report_event(device_event); return IRQ_HANDLED; } diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 54d31c0a9840..e946c0d58a97 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -2282,10 +2282,6 @@ static struct tp_acpi_drv_struct ibm_hotkey_acpidriver; static void tpacpi_hotkey_send_key(unsigned int scancode) { tpacpi_input_send_key_masked(scancode); - if (hotkey_report_mode < 2) { - acpi_bus_generate_proc_event(ibm_hotkey_acpidriver.device, - 0x80, TP_HKEY_EV_HOTKEY_BASE + scancode); - } } static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m) @@ -3737,13 +3733,6 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) "event happened to %s\n", TPACPI_MAIL); } - /* Legacy events */ - if (!ignore_acpi_ev && - (send_acpi_ev || hotkey_report_mode < 2)) { - acpi_bus_generate_proc_event(ibm->acpi->device, - event, hkey); - } - /* netlink events */ if (!ignore_acpi_ev && send_acpi_ev) { acpi_bus_generate_netlink_event( diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 56e6b68c8d2f..2650d1f19e43 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -383,15 +383,6 @@ bool acpi_bus_can_wakeup(acpi_handle handle); static inline bool acpi_bus_can_wakeup(acpi_handle handle) { return false; } #endif -#ifdef CONFIG_ACPI_PROC_EVENT -int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data); -int acpi_bus_generate_proc_event4(const char *class, const char *bid, u8 type, int data); -int acpi_bus_receive_event(struct acpi_bus_event *event); -#else -static inline int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data) - { return 0; } -#endif - void acpi_scan_lock_acquire(void); void acpi_scan_lock_release(void); int acpi_scan_add_handler(struct acpi_scan_handler *handler); -- cgit v1.2.3-71-gd317 From 221a27c76033a3a4196b3da09848bc5f237f3f94 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 8 Jul 2013 14:15:25 +0530 Subject: dmaengine: add dma_slave_get_caps api add new device callback .device_slave_caps api which can be used by clients to query the dma channel capablties before they program the channel. This can help is removing errors during the channel programming. Also add helper dma_slave_get_caps API This patch folds the work done by Matt earlier https://patchwork.kernel.org/patch/2094891/ Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index cb286b1acdb6..5692bc3afd39 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -370,6 +370,33 @@ struct dma_slave_config { unsigned int slave_id; }; +/* struct dma_slave_caps - expose capabilities of a slave channel only + * + * @src_addr_widths: bit mask of src addr widths the channel supports + * @dstn_addr_widths: bit mask of dstn addr widths the channel supports + * @directions: bit mask of slave direction the channel supported + * since the enum dma_transfer_direction is not defined as bits for each + * type of direction, the dma controller should fill (1 << ) and same + * should be checked by controller as well + * @cmd_pause: true, if pause and thereby resume is supported + * @cmd_terminate: true, if terminate cmd is supported + * + * @max_sg_nr: maximum number of SG segments supported + * 0 for no maximum + * @max_sg_len: maximum length of a SG segment supported + * 0 for no maximum + */ +struct dma_slave_caps { + u32 src_addr_widths; + u32 dstn_addr_widths; + u32 directions; + bool cmd_pause; + bool cmd_terminate; + + u32 max_sg_nr; + u32 max_sg_len; +}; + static inline const char *dma_chan_name(struct dma_chan *chan) { return dev_name(&chan->dev->device); @@ -532,6 +559,7 @@ struct dma_tx_state { * struct with auxiliary transfer status information, otherwise the call * will just return a simple status code * @device_issue_pending: push pending transactions to hardware + * @device_slave_caps: return the slave channel capabilities */ struct dma_device { @@ -597,6 +625,7 @@ struct dma_device { dma_cookie_t cookie, struct dma_tx_state *txstate); void (*device_issue_pending)(struct dma_chan *chan); + int (*device_slave_caps)(struct dma_chan *chan, struct dma_slave_caps *caps); }; static inline int dmaengine_device_control(struct dma_chan *chan, @@ -670,6 +699,21 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma( return chan->device->device_prep_interleaved_dma(chan, xt, flags); } +static inline int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps) +{ + if (!chan || !caps) + return -EINVAL; + + /* check if the channel supports slave transactions */ + if (!test_bit(DMA_SLAVE, chan->device->cap_mask.bits)) + return -ENXIO; + + if (chan->device->device_slave_caps) + return chan->device->device_slave_caps(chan, caps); + + return -ENXIO; +} + static inline int dmaengine_terminate_all(struct dma_chan *chan) { return dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0); -- cgit v1.2.3-71-gd317 From 939c02777a27ea7915764ecca5263dbb60c664f3 Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Sat, 29 Jun 2013 18:21:16 +0530 Subject: regulator: s2mps11: Implement set_ramp_rate callback for bucks Implementing set_ramp_rate() and using standard constraints for getting ramp_delay and ramp_disable, instead of getting it as s2mps11 specific data through platform data, makes driver more compliant with framework and reduces the complexity for adding DT support. Signed-off-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- drivers/regulator/s2mps11.c | 117 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/samsung/s2mps11.h | 11 ++++ 2 files changed, 128 insertions(+) (limited to 'include') diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index a671eb63a4e5..4b84e7660637 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -104,6 +105,121 @@ static int s2mps11_regulator_set_voltage_time_sel(struct regulator_dev *rdev, return DIV_ROUND_UP(abs(new_volt - old_volt), ramp_delay); } +static int s2mps11_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev); + unsigned int ramp_val, ramp_shift, ramp_reg = S2MPS11_REG_RAMP_BUCK; + unsigned int ramp_enable = 1, enable_shift = 0; + int ret; + + switch (rdev->desc->id) { + case S2MPS11_BUCK1: + if (ramp_delay > s2mps11->ramp_delay16) + s2mps11->ramp_delay16 = ramp_delay; + else + ramp_delay = s2mps11->ramp_delay16; + + ramp_shift = S2MPS11_BUCK16_RAMP_SHIFT; + break; + case S2MPS11_BUCK2: + enable_shift = S2MPS11_BUCK2_RAMP_EN_SHIFT; + if (!ramp_delay) { + ramp_enable = 0; + break; + } + + s2mps11->ramp_delay2 = ramp_delay; + ramp_shift = S2MPS11_BUCK2_RAMP_SHIFT; + ramp_reg = S2MPS11_REG_RAMP; + break; + case S2MPS11_BUCK3: + enable_shift = S2MPS11_BUCK3_RAMP_EN_SHIFT; + if (!ramp_delay) { + ramp_enable = 0; + break; + } + + if (ramp_delay > s2mps11->ramp_delay34) + s2mps11->ramp_delay34 = ramp_delay; + else + ramp_delay = s2mps11->ramp_delay34; + + ramp_shift = S2MPS11_BUCK34_RAMP_SHIFT; + ramp_reg = S2MPS11_REG_RAMP; + break; + case S2MPS11_BUCK4: + enable_shift = S2MPS11_BUCK4_RAMP_EN_SHIFT; + if (!ramp_delay) { + ramp_enable = 0; + break; + } + + if (ramp_delay > s2mps11->ramp_delay34) + s2mps11->ramp_delay34 = ramp_delay; + else + ramp_delay = s2mps11->ramp_delay34; + + ramp_shift = S2MPS11_BUCK34_RAMP_SHIFT; + ramp_reg = S2MPS11_REG_RAMP; + break; + case S2MPS11_BUCK5: + s2mps11->ramp_delay5 = ramp_delay; + ramp_shift = S2MPS11_BUCK5_RAMP_SHIFT; + break; + case S2MPS11_BUCK6: + enable_shift = S2MPS11_BUCK6_RAMP_EN_SHIFT; + if (!ramp_delay) { + ramp_enable = 0; + break; + } + + if (ramp_delay > s2mps11->ramp_delay16) + s2mps11->ramp_delay16 = ramp_delay; + else + ramp_delay = s2mps11->ramp_delay16; + + ramp_shift = S2MPS11_BUCK16_RAMP_SHIFT; + break; + case S2MPS11_BUCK7: + case S2MPS11_BUCK8: + case S2MPS11_BUCK10: + if (ramp_delay > s2mps11->ramp_delay7810) + s2mps11->ramp_delay7810 = ramp_delay; + else + ramp_delay = s2mps11->ramp_delay7810; + + ramp_shift = S2MPS11_BUCK7810_RAMP_SHIFT; + break; + case S2MPS11_BUCK9: + s2mps11->ramp_delay9 = ramp_delay; + ramp_shift = S2MPS11_BUCK9_RAMP_SHIFT; + break; + default: + return 0; + } + + if (!ramp_enable) + goto ramp_disable; + + if (enable_shift) { + ret = regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP, + 1 << enable_shift, 1 << enable_shift); + if (ret) { + dev_err(&rdev->dev, "failed to enable ramp rate\n"); + return ret; + } + } + + ramp_val = get_ramp_delay(ramp_delay); + + return regmap_update_bits(rdev->regmap, ramp_reg, + ramp_val << ramp_shift, 1 << ramp_shift); + +ramp_disable: + return regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP, 0, + 1 << enable_shift); +} + static struct regulator_ops s2mps11_ldo_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, @@ -124,6 +240,7 @@ static struct regulator_ops s2mps11_buck_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = s2mps11_regulator_set_voltage_time_sel, + .set_ramp_delay = s2mps11_set_ramp_delay, }; #define regulator_desc_ldo1(num) { \ diff --git a/include/linux/mfd/samsung/s2mps11.h b/include/linux/mfd/samsung/s2mps11.h index 4e94dc65f987..d0d52ea60074 100644 --- a/include/linux/mfd/samsung/s2mps11.h +++ b/include/linux/mfd/samsung/s2mps11.h @@ -191,6 +191,17 @@ enum s2mps11_regulators { #define S2MPS11_BUCK_N_VOLTAGES (S2MPS11_BUCK_VSEL_MASK + 1) #define S2MPS11_RAMP_DELAY 25000 /* uV/us */ + +#define S2MPS11_BUCK2_RAMP_SHIFT 6 +#define S2MPS11_BUCK34_RAMP_SHIFT 4 +#define S2MPS11_BUCK5_RAMP_SHIFT 6 +#define S2MPS11_BUCK16_RAMP_SHIFT 4 +#define S2MPS11_BUCK7810_RAMP_SHIFT 2 +#define S2MPS11_BUCK9_RAMP_SHIFT 0 +#define S2MPS11_BUCK2_RAMP_EN_SHIFT 3 +#define S2MPS11_BUCK3_RAMP_EN_SHIFT 2 +#define S2MPS11_BUCK4_RAMP_EN_SHIFT 1 +#define S2MPS11_BUCK6_RAMP_EN_SHIFT 0 #define S2MPS11_PMIC_EN_SHIFT 6 #define S2MPS11_REGULATOR_MAX (S2MPS11_REG_MAX - 3) -- cgit v1.2.3-71-gd317 From 8b770e3c9824c98eafe67950ad6e41e09ec9c98a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 4 Jul 2013 21:13:24 +0200 Subject: backlight: Add GPIO-based backlight driver The GPIO backlight driver controls the backlight in on/off mode through a single GPIO. Signed-off-by: Laurent Pinchart Acked-by: Jingoo Han Signed-off-by: Simon Horman --- drivers/video/backlight/Kconfig | 7 ++ drivers/video/backlight/Makefile | 1 + drivers/video/backlight/gpio_backlight.c | 133 +++++++++++++++++++++++++++ include/linux/platform_data/gpio_backlight.h | 21 +++++ 4 files changed, 162 insertions(+) create mode 100644 drivers/video/backlight/gpio_backlight.c create mode 100644 include/linux/platform_data/gpio_backlight.h (limited to 'include') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index d5ab6583f440..5ab64237c972 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -425,6 +425,13 @@ config BACKLIGHT_AS3711 If you have an Austrian Microsystems AS3711 say Y to enable the backlight driver. +config BACKLIGHT_GPIO + tristate "Generic GPIO based Backlight Driver" + depends on GPIOLIB + help + If you have a LCD backlight adjustable by GPIO, say Y to enable + this driver. + endif # BACKLIGHT_CLASS_DEVICE endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 92711fe60464..65698a882c2f 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o obj-$(CONFIG_BACKLIGHT_DA9052) += da9052_bl.o obj-$(CONFIG_BACKLIGHT_EP93XX) += ep93xx_bl.o obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o +obj-$(CONFIG_BACKLIGHT_GPIO) += gpio_backlight.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c new file mode 100644 index 000000000000..5fa217f9f445 --- /dev/null +++ b/drivers/video/backlight/gpio_backlight.c @@ -0,0 +1,133 @@ +/* + * gpio_backlight.c - Simple GPIO-controlled backlight + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpio_backlight { + struct device *dev; + struct device *fbdev; + + int gpio; + int active; +}; + +static int gpio_backlight_update_status(struct backlight_device *bl) +{ + struct gpio_backlight *gbl = bl_get_data(bl); + int brightness = bl->props.brightness; + + if (bl->props.power != FB_BLANK_UNBLANK || + bl->props.fb_blank != FB_BLANK_UNBLANK || + bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) + brightness = 0; + + gpio_set_value(gbl->gpio, brightness ? gbl->active : !gbl->active); + + return 0; +} + +static int gpio_backlight_get_brightness(struct backlight_device *bl) +{ + return bl->props.brightness; +} + +static int gpio_backlight_check_fb(struct backlight_device *bl, + struct fb_info *info) +{ + struct gpio_backlight *gbl = bl_get_data(bl); + + return gbl->fbdev == NULL || gbl->fbdev == info->dev; +} + +static const struct backlight_ops gpio_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = gpio_backlight_update_status, + .get_brightness = gpio_backlight_get_brightness, + .check_fb = gpio_backlight_check_fb, +}; + +static int gpio_backlight_probe(struct platform_device *pdev) +{ + struct gpio_backlight_platform_data *pdata = pdev->dev.platform_data; + struct backlight_properties props; + struct backlight_device *bl; + struct gpio_backlight *gbl; + int ret; + + if (!pdata) { + dev_err(&pdev->dev, "failed to find platform data\n"); + return -ENODEV; + } + + gbl = devm_kzalloc(&pdev->dev, sizeof(*gbl), GFP_KERNEL); + if (gbl == NULL) + return -ENOMEM; + + gbl->dev = &pdev->dev; + gbl->fbdev = pdata->fbdev; + gbl->gpio = pdata->gpio; + gbl->active = pdata->active_low ? 0 : 1; + + ret = devm_gpio_request_one(gbl->dev, gbl->gpio, GPIOF_DIR_OUT | + (gbl->active ? GPIOF_INIT_LOW + : GPIOF_INIT_HIGH), + pdata->name); + if (ret < 0) { + dev_err(&pdev->dev, "unable to request GPIO\n"); + return ret; + } + + memset(&props, 0, sizeof(props)); + props.type = BACKLIGHT_RAW; + props.max_brightness = 1; + bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, gbl, + &gpio_backlight_ops, &props); + if (IS_ERR(bl)) { + dev_err(&pdev->dev, "failed to register backlight\n"); + return PTR_ERR(bl); + } + + bl->props.brightness = pdata->def_value; + backlight_update_status(bl); + + platform_set_drvdata(pdev, bl); + return 0; +} + +static int gpio_backlight_remove(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + + backlight_device_unregister(bl); + return 0; +} + +static struct platform_driver gpio_backlight_driver = { + .driver = { + .name = "gpio-backlight", + .owner = THIS_MODULE, + }, + .probe = gpio_backlight_probe, + .remove = gpio_backlight_remove, +}; + +module_platform_driver(gpio_backlight_driver); + +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("GPIO-based Backlight Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio-backlight"); diff --git a/include/linux/platform_data/gpio_backlight.h b/include/linux/platform_data/gpio_backlight.h new file mode 100644 index 000000000000..5ae0d9c80d4d --- /dev/null +++ b/include/linux/platform_data/gpio_backlight.h @@ -0,0 +1,21 @@ +/* + * gpio_backlight.h - Simple GPIO-controlled backlight + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __GPIO_BACKLIGHT_H__ +#define __GPIO_BACKLIGHT_H__ + +struct device; + +struct gpio_backlight_platform_data { + struct device *fbdev; + int gpio; + int def_value; + bool active_low; + const char *name; +}; + +#endif -- cgit v1.2.3-71-gd317 From 82e5c40d88f928afffe7bd4027719d3184433908 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 4 Jul 2013 21:13:25 +0200 Subject: backlight: Add Sanyo LV5207LP backlight driver The LV5207LP is a multi-purpose 7 LEDs driver for the mobile market. Only the main LED is supported by this driver. Signed-off-by: Laurent Pinchart Acked-by: Jingoo Han Signed-off-by: Simon Horman --- drivers/video/backlight/Kconfig | 6 ++ drivers/video/backlight/Makefile | 1 + drivers/video/backlight/lv5207lp.c | 171 +++++++++++++++++++++++++++++++++ include/linux/platform_data/lv5207lp.h | 19 ++++ 4 files changed, 197 insertions(+) create mode 100644 drivers/video/backlight/lv5207lp.c create mode 100644 include/linux/platform_data/lv5207lp.h (limited to 'include') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 5ab64237c972..b304e075cf3a 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -432,6 +432,12 @@ config BACKLIGHT_GPIO If you have a LCD backlight adjustable by GPIO, say Y to enable this driver. +config BACKLIGHT_LV5207LP + tristate "Sanyo LV5207LP Backlight" + depends on I2C + help + If you have a Sanyo LV5207LP say Y to enable the backlight driver. + endif # BACKLIGHT_CLASS_DEVICE endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 65698a882c2f..4e5e8116c8c4 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o obj-$(CONFIG_BACKLIGHT_LP8788) += lp8788_bl.o +obj-$(CONFIG_BACKLIGHT_LV5207LP) += lv5207lp.o obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o diff --git a/drivers/video/backlight/lv5207lp.c b/drivers/video/backlight/lv5207lp.c new file mode 100644 index 000000000000..498fd73d59b9 --- /dev/null +++ b/drivers/video/backlight/lv5207lp.c @@ -0,0 +1,171 @@ +/* + * Sanyo LV5207LP LED Driver + * + * Copyright (C) 2013 Ideas on board SPRL + * + * Contact: Laurent Pinchart + * + * 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 +#include +#include +#include +#include +#include +#include + +#define LV5207LP_CTRL1 0x00 +#define LV5207LP_CPSW (1 << 7) +#define LV5207LP_SCTEN (1 << 6) +#define LV5207LP_C10 (1 << 5) +#define LV5207LP_CKSW (1 << 4) +#define LV5207LP_RSW (1 << 3) +#define LV5207LP_GSW (1 << 2) +#define LV5207LP_BSW (1 << 1) +#define LV5207LP_CTRL2 0x01 +#define LV5207LP_MSW (1 << 7) +#define LV5207LP_MLED4 (1 << 6) +#define LV5207LP_RED 0x02 +#define LV5207LP_GREEN 0x03 +#define LV5207LP_BLUE 0x04 + +#define LV5207LP_MAX_BRIGHTNESS 32 + +struct lv5207lp { + struct i2c_client *client; + struct backlight_device *backlight; + struct lv5207lp_platform_data *pdata; +}; + +static int lv5207lp_write(struct lv5207lp *lv, u8 reg, u8 data) +{ + return i2c_smbus_write_byte_data(lv->client, reg, data); +} + +static int lv5207lp_backlight_update_status(struct backlight_device *backlight) +{ + struct lv5207lp *lv = bl_get_data(backlight); + int brightness = backlight->props.brightness; + + if (backlight->props.power != FB_BLANK_UNBLANK || + backlight->props.fb_blank != FB_BLANK_UNBLANK || + backlight->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) + brightness = 0; + + if (brightness) { + lv5207lp_write(lv, LV5207LP_CTRL1, + LV5207LP_CPSW | LV5207LP_C10 | LV5207LP_CKSW); + lv5207lp_write(lv, LV5207LP_CTRL2, + LV5207LP_MSW | LV5207LP_MLED4 | + (brightness - 1)); + } else { + lv5207lp_write(lv, LV5207LP_CTRL1, 0); + lv5207lp_write(lv, LV5207LP_CTRL2, 0); + } + + return 0; +} + +static int lv5207lp_backlight_get_brightness(struct backlight_device *backlight) +{ + return backlight->props.brightness; +} + +static int lv5207lp_backlight_check_fb(struct backlight_device *backlight, + struct fb_info *info) +{ + struct lv5207lp *lv = bl_get_data(backlight); + + return lv->pdata->fbdev == NULL || lv->pdata->fbdev == info->dev; +} + +static const struct backlight_ops lv5207lp_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = lv5207lp_backlight_update_status, + .get_brightness = lv5207lp_backlight_get_brightness, + .check_fb = lv5207lp_backlight_check_fb, +}; + +static int lv5207lp_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lv5207lp_platform_data *pdata = client->dev.platform_data; + struct backlight_device *backlight; + struct backlight_properties props; + struct lv5207lp *lv; + + if (pdata == NULL) { + dev_err(&client->dev, "No platform data supplied\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_warn(&client->dev, + "I2C adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); + return -EIO; + } + + lv = devm_kzalloc(&client->dev, sizeof(*lv), GFP_KERNEL); + if (!lv) + return -ENOMEM; + + lv->client = client; + lv->pdata = pdata; + + memset(&props, 0, sizeof(props)); + props.type = BACKLIGHT_RAW; + props.max_brightness = min_t(unsigned int, pdata->max_value, + LV5207LP_MAX_BRIGHTNESS); + props.brightness = clamp_t(unsigned int, pdata->def_value, 0, + props.max_brightness); + + backlight = backlight_device_register(dev_name(&client->dev), + &lv->client->dev, lv, + &lv5207lp_backlight_ops, &props); + if (IS_ERR(backlight)) { + dev_err(&client->dev, "failed to register backlight\n"); + return PTR_ERR(backlight); + } + + backlight_update_status(backlight); + i2c_set_clientdata(client, backlight); + + return 0; +} + +static int lv5207lp_remove(struct i2c_client *client) +{ + struct backlight_device *backlight = i2c_get_clientdata(client); + + backlight->props.brightness = 0; + backlight_update_status(backlight); + backlight_device_unregister(backlight); + + return 0; +} + +static const struct i2c_device_id lv5207lp_ids[] = { + { "lv5207lp", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lv5207lp_ids); + +static struct i2c_driver lv5207lp_driver = { + .driver = { + .name = "lv5207lp", + }, + .probe = lv5207lp_probe, + .remove = lv5207lp_remove, + .id_table = lv5207lp_ids, +}; + +module_i2c_driver(lv5207lp_driver); + +MODULE_DESCRIPTION("Sanyo LV5207LP Backlight Driver"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/lv5207lp.h b/include/linux/platform_data/lv5207lp.h new file mode 100644 index 000000000000..7dc4d9a219a6 --- /dev/null +++ b/include/linux/platform_data/lv5207lp.h @@ -0,0 +1,19 @@ +/* + * lv5207lp.h - Sanyo LV5207LP LEDs Driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __LV5207LP_H__ +#define __LV5207LP_H__ + +struct device; + +struct lv5207lp_platform_data { + struct device *fbdev; + unsigned int max_value; + unsigned int def_value; +}; + +#endif -- cgit v1.2.3-71-gd317 From 67b43e590415866649e5ba8a6421bb84ecb74f72 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 4 Jul 2013 21:13:26 +0200 Subject: backlight: Add ROHM BD6107 backlight driver The BD6107 is a multi-purpose 10 channels LED driver for the mobile market. Only the main channel is supported by this driver. Signed-off-by: Laurent Pinchart Acked-by: Jingoo Han Signed-off-by: Simon Horman --- drivers/video/backlight/Kconfig | 6 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/bd6107.c | 213 +++++++++++++++++++++++++++++++++++ include/linux/platform_data/bd6107.h | 19 ++++ 4 files changed, 239 insertions(+) create mode 100644 drivers/video/backlight/bd6107.c create mode 100644 include/linux/platform_data/bd6107.h (limited to 'include') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index b304e075cf3a..d4a7a351d67c 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -438,6 +438,12 @@ config BACKLIGHT_LV5207LP help If you have a Sanyo LV5207LP say Y to enable the backlight driver. +config BACKLIGHT_BD6107 + tristate "Rohm BD6107 Backlight" + depends on I2C + help + If you have a Rohm BD6107 say Y to enable the backlight driver. + endif # BACKLIGHT_CLASS_DEVICE endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 4e5e8116c8c4..38e1babb1946 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o obj-$(CONFIG_BACKLIGHT_AS3711) += as3711_bl.o obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o +obj-$(CONFIG_BACKLIGHT_BD6107) += bd6107.o obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o diff --git a/drivers/video/backlight/bd6107.c b/drivers/video/backlight/bd6107.c new file mode 100644 index 000000000000..15e3294b29fe --- /dev/null +++ b/drivers/video/backlight/bd6107.c @@ -0,0 +1,213 @@ +/* + * ROHM Semiconductor BD6107 LED Driver + * + * Copyright (C) 2013 Ideas on board SPRL + * + * Contact: Laurent Pinchart + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define BD6107_PSCNT1 0x00 +#define BD6107_PSCNT1_PSCNTREG2 (1 << 2) +#define BD6107_PSCNT1_PSCNTREG1 (1 << 0) +#define BD6107_REGVSET 0x02 +#define BD6107_REGVSET_REG1VSET_2_85V (1 << 2) +#define BD6107_REGVSET_REG1VSET_2_80V (0 << 2) +#define BD6107_LEDCNT1 0x03 +#define BD6107_LEDCNT1_LEDONOFF2 (1 << 1) +#define BD6107_LEDCNT1_LEDONOFF1 (1 << 0) +#define BD6107_PORTSEL 0x04 +#define BD6107_PORTSEL_LEDM(n) (1 << (n)) +#define BD6107_RGB1CNT1 0x05 +#define BD6107_RGB1CNT2 0x06 +#define BD6107_RGB1CNT3 0x07 +#define BD6107_RGB1CNT4 0x08 +#define BD6107_RGB1CNT5 0x09 +#define BD6107_RGB1FLM 0x0a +#define BD6107_RGB2CNT1 0x0b +#define BD6107_RGB2CNT2 0x0c +#define BD6107_RGB2CNT3 0x0d +#define BD6107_RGB2CNT4 0x0e +#define BD6107_RGB2CNT5 0x0f +#define BD6107_RGB2FLM 0x10 +#define BD6107_PSCONT3 0x11 +#define BD6107_SMMONCNT 0x12 +#define BD6107_DCDCCNT 0x13 +#define BD6107_IOSEL 0x14 +#define BD6107_OUT1 0x15 +#define BD6107_OUT2 0x16 +#define BD6107_MASK1 0x17 +#define BD6107_MASK2 0x18 +#define BD6107_FACTOR1 0x19 +#define BD6107_FACTOR2 0x1a +#define BD6107_CLRFACT1 0x1b +#define BD6107_CLRFACT2 0x1c +#define BD6107_STATE1 0x1d +#define BD6107_LSIVER 0x1e +#define BD6107_GRPSEL 0x1f +#define BD6107_LEDCNT2 0x20 +#define BD6107_LEDCNT3 0x21 +#define BD6107_MCURRENT 0x22 +#define BD6107_MAINCNT1 0x23 +#define BD6107_MAINCNT2 0x24 +#define BD6107_SLOPECNT 0x25 +#define BD6107_MSLOPE 0x26 +#define BD6107_RGBSLOPE 0x27 +#define BD6107_TEST 0x29 +#define BD6107_SFTRST 0x2a +#define BD6107_SFTRSTGD 0x2b + +struct bd6107 { + struct i2c_client *client; + struct backlight_device *backlight; + struct bd6107_platform_data *pdata; +}; + +static int bd6107_write(struct bd6107 *bd, u8 reg, u8 data) +{ + return i2c_smbus_write_byte_data(bd->client, reg, data); +} + +static int bd6107_backlight_update_status(struct backlight_device *backlight) +{ + struct bd6107 *bd = bl_get_data(backlight); + int brightness = backlight->props.brightness; + + if (backlight->props.power != FB_BLANK_UNBLANK || + backlight->props.fb_blank != FB_BLANK_UNBLANK || + backlight->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) + brightness = 0; + + if (brightness) { + bd6107_write(bd, BD6107_PORTSEL, BD6107_PORTSEL_LEDM(2) | + BD6107_PORTSEL_LEDM(1) | BD6107_PORTSEL_LEDM(0)); + bd6107_write(bd, BD6107_MAINCNT1, brightness); + bd6107_write(bd, BD6107_LEDCNT1, BD6107_LEDCNT1_LEDONOFF1); + } else { + gpio_set_value(bd->pdata->reset, 0); + msleep(24); + gpio_set_value(bd->pdata->reset, 1); + } + + return 0; +} + +static int bd6107_backlight_get_brightness(struct backlight_device *backlight) +{ + return backlight->props.brightness; +} + +static int bd6107_backlight_check_fb(struct backlight_device *backlight, + struct fb_info *info) +{ + struct bd6107 *bd = bl_get_data(backlight); + + return bd->pdata->fbdev == NULL || bd->pdata->fbdev == info->dev; +} + +static const struct backlight_ops bd6107_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = bd6107_backlight_update_status, + .get_brightness = bd6107_backlight_get_brightness, + .check_fb = bd6107_backlight_check_fb, +}; + +static int bd6107_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bd6107_platform_data *pdata = client->dev.platform_data; + struct backlight_device *backlight; + struct backlight_properties props; + struct bd6107 *bd; + int ret; + + if (pdata == NULL || !pdata->reset) { + dev_err(&client->dev, "No reset GPIO in platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_warn(&client->dev, + "I2C adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); + return -EIO; + } + + bd = devm_kzalloc(&client->dev, sizeof(*bd), GFP_KERNEL); + if (!bd) + return -ENOMEM; + + bd->client = client; + bd->pdata = pdata; + + ret = devm_gpio_request_one(&client->dev, pdata->reset, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, "reset"); + if (ret < 0) { + dev_err(&client->dev, "unable to request reset GPIO\n"); + return ret; + } + + memset(&props, 0, sizeof(props)); + props.type = BACKLIGHT_RAW; + props.max_brightness = 128; + props.brightness = clamp_t(unsigned int, pdata->def_value, 0, + props.max_brightness); + + backlight = backlight_device_register(dev_name(&client->dev), + &bd->client->dev, bd, + &bd6107_backlight_ops, &props); + if (IS_ERR(backlight)) { + dev_err(&client->dev, "failed to register backlight\n"); + return PTR_ERR(backlight); + } + + backlight_update_status(backlight); + i2c_set_clientdata(client, backlight); + + return 0; +} + +static int bd6107_remove(struct i2c_client *client) +{ + struct backlight_device *backlight = i2c_get_clientdata(client); + + backlight->props.brightness = 0; + backlight_update_status(backlight); + backlight_device_unregister(backlight); + + return 0; +} + +static const struct i2c_device_id bd6107_ids[] = { + { "bd6107", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bd6107_ids); + +static struct i2c_driver bd6107_driver = { + .driver = { + .name = "bd6107", + }, + .probe = bd6107_probe, + .remove = bd6107_remove, + .id_table = bd6107_ids, +}; + +module_i2c_driver(bd6107_driver); + +MODULE_DESCRIPTION("Rohm BD6107 Backlight Driver"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/bd6107.h b/include/linux/platform_data/bd6107.h new file mode 100644 index 000000000000..671d6502d241 --- /dev/null +++ b/include/linux/platform_data/bd6107.h @@ -0,0 +1,19 @@ +/* + * bd6107.h - Rohm BD6107 LEDs Driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __BD6107_H__ +#define __BD6107_H__ + +struct device; + +struct bd6107_platform_data { + struct device *fbdev; + int reset; /* Reset GPIO */ + unsigned int def_value; +}; + +#endif -- cgit v1.2.3-71-gd317 From 50ac6607845755e594c8a39b9c6a00d1c9b48ea4 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Tue, 25 Jun 2013 19:03:56 -0700 Subject: cfg80211/nl80211: rename packet pattern related structures and enums Currently packet patterns and it's enum/structures are used only for WoWLAN feature. As we intend to reuse them for new feature packet coalesce, they are renamed in this patch. Older names are kept for backward compatibility purpose. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath9k/main.c | 2 +- drivers/net/wireless/mwifiex/cfg80211.c | 3 +-- drivers/net/wireless/ti/wlcore/main.c | 10 ++++---- include/net/cfg80211.h | 6 ++--- include/uapi/linux/nl80211.h | 45 ++++++++++++++++++++------------- net/wireless/nl80211.c | 34 ++++++++++++------------- 6 files changed, 53 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 1737a3e33685..1adb803a9d86 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2094,7 +2094,7 @@ static void ath9k_wow_add_pattern(struct ath_softc *sc, { struct ath_hw *ah = sc->sc_ah; struct ath9k_wow_pattern *wow_pattern = NULL; - struct cfg80211_wowlan_trig_pkt_pattern *patterns = wowlan->patterns; + struct cfg80211_pkt_pattern *patterns = wowlan->patterns; int mask_len; s8 i = 0; diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index ef5fa890a286..0bd631fd556c 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -2298,8 +2298,7 @@ EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf); #ifdef CONFIG_PM static bool -mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat, - s8 *byte_seq) +mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq) { int j, k, valid_byte_cnt = 0; bool dont_care_byte = false; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index b8db55c868c7..d1b19c38a907 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1315,7 +1315,7 @@ static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) #ifdef CONFIG_PM static int -wl1271_validate_wowlan_pattern(struct cfg80211_wowlan_trig_pkt_pattern *p) +wl1271_validate_wowlan_pattern(struct cfg80211_pkt_pattern *p) { int num_fields = 0, in_field = 0, fields_size = 0; int i, pattern_len = 0; @@ -1458,9 +1458,9 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter, * Allocates an RX filter returned through f * which needs to be freed using rx_filter_free() */ -static int wl1271_convert_wowlan_pattern_to_rx_filter( - struct cfg80211_wowlan_trig_pkt_pattern *p, - struct wl12xx_rx_filter **f) +static int +wl1271_convert_wowlan_pattern_to_rx_filter(struct cfg80211_pkt_pattern *p, + struct wl12xx_rx_filter **f) { int i, j, ret = 0; struct wl12xx_rx_filter *filter; @@ -1562,7 +1562,7 @@ static int wl1271_configure_wowlan(struct wl1271 *wl, /* Translate WoWLAN patterns into filters */ for (i = 0; i < wow->n_patterns; i++) { - struct cfg80211_wowlan_trig_pkt_pattern *p; + struct cfg80211_pkt_pattern *p; struct wl12xx_rx_filter *filter = NULL; p = &wow->patterns[i]; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7b0730aeb892..207b079a8fa5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1698,7 +1698,7 @@ struct cfg80211_pmksa { }; /** - * struct cfg80211_wowlan_trig_pkt_pattern - packet pattern + * struct cfg80211_pkt_pattern - packet pattern * @mask: bitmask where to match pattern and where to ignore bytes, * one bit per byte, in same format as nl80211 * @pattern: bytes to match where bitmask is 1 @@ -1708,7 +1708,7 @@ struct cfg80211_pmksa { * Internal note: @mask and @pattern are allocated in one chunk of * memory, free @mask only! */ -struct cfg80211_wowlan_trig_pkt_pattern { +struct cfg80211_pkt_pattern { u8 *mask, *pattern; int pattern_len; int pkt_offset; @@ -1770,7 +1770,7 @@ struct cfg80211_wowlan { bool any, disconnect, magic_pkt, gtk_rekey_failure, eap_identity_req, four_way_handshake, rfkill_release; - struct cfg80211_wowlan_trig_pkt_pattern *patterns; + struct cfg80211_pkt_pattern *patterns; struct cfg80211_wowlan_tcp *tcp; int n_patterns; }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 861e5eba3953..de0ce809068a 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3060,11 +3060,11 @@ enum nl80211_tx_power_setting { }; /** - * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute - * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute - * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has + * enum nl80211_packet_pattern_attr - packet pattern attribute + * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute + * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has * a zero bit are ignored - * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have + * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have * a bit for each byte in the pattern. The lowest-order bit corresponds * to the first byte of the pattern, but the bytes of the pattern are * in a little-endian-like format, i.e. the 9th byte of the pattern @@ -3075,23 +3075,23 @@ enum nl80211_tx_power_setting { * Note that the pattern matching is done as though frames were not * 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked * first (including SNAP header unpacking) and then matched. - * @NL80211_WOWLAN_PKTPAT_OFFSET: packet offset, pattern is matched after + * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after * these fixed number of bytes of received packet - * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes - * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number + * @NUM_NL80211_PKTPAT: number of attributes + * @MAX_NL80211_PKTPAT: max attribute number */ -enum nl80211_wowlan_packet_pattern_attr { - __NL80211_WOWLAN_PKTPAT_INVALID, - NL80211_WOWLAN_PKTPAT_MASK, - NL80211_WOWLAN_PKTPAT_PATTERN, - NL80211_WOWLAN_PKTPAT_OFFSET, +enum nl80211_packet_pattern_attr { + __NL80211_PKTPAT_INVALID, + NL80211_PKTPAT_MASK, + NL80211_PKTPAT_PATTERN, + NL80211_PKTPAT_OFFSET, - NUM_NL80211_WOWLAN_PKTPAT, - MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1, + NUM_NL80211_PKTPAT, + MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1, }; /** - * struct nl80211_wowlan_pattern_support - pattern support information + * struct nl80211_pattern_support - packet pattern support information * @max_patterns: maximum number of patterns supported * @min_pattern_len: minimum length of each pattern * @max_pattern_len: maximum length of each pattern @@ -3101,13 +3101,22 @@ enum nl80211_wowlan_packet_pattern_attr { * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the * capability information given by the kernel to userspace. */ -struct nl80211_wowlan_pattern_support { +struct nl80211_pattern_support { __u32 max_patterns; __u32 min_pattern_len; __u32 max_pattern_len; __u32 max_pkt_offset; } __attribute__((packed)); +/* only for backward compatibility */ +#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID +#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK +#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN +#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET +#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT +#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT +#define nl80211_wowlan_pattern_support nl80211_pattern_support + /** * enum nl80211_wowlan_triggers - WoWLAN trigger definitions * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes @@ -3127,7 +3136,7 @@ struct nl80211_wowlan_pattern_support { * pattern matching is done after the packet is converted to the MSDU. * * In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute - * carrying a &struct nl80211_wowlan_pattern_support. + * carrying a &struct nl80211_pattern_support. * * When reporting wakeup. it is a u32 attribute containing the 0-based * index of the pattern that caused the wakeup, in the patterns passed @@ -3284,7 +3293,7 @@ struct nl80211_wowlan_tcp_data_token_feature { * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a * u32 attribute holding the maximum length * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for - * feature advertising. The mask works like @NL80211_WOWLAN_PKTPAT_MASK + * feature advertising. The mask works like @NL80211_PKTPAT_MASK * but on the TCP payload only. * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes * @MAX_NL80211_WOWLAN_TCP: highest attribute number diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1cc47aca7f05..a044762f5ea3 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -974,7 +974,7 @@ static int nl80211_send_wowlan(struct sk_buff *msg, return -ENOBUFS; if (dev->wiphy.wowlan->n_patterns) { - struct nl80211_wowlan_pattern_support pat = { + struct nl80211_pattern_support pat = { .max_patterns = dev->wiphy.wowlan->n_patterns, .min_pattern_len = dev->wiphy.wowlan->pattern_min_len, .max_pattern_len = dev->wiphy.wowlan->pattern_max_len, @@ -7591,12 +7591,11 @@ static int nl80211_send_wowlan_patterns(struct sk_buff *msg, if (!nl_pat) return -ENOBUFS; pat_len = wowlan->patterns[i].pattern_len; - if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK, - DIV_ROUND_UP(pat_len, 8), + if (nla_put(msg, NL80211_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8), wowlan->patterns[i].mask) || - nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN, - pat_len, wowlan->patterns[i].pattern) || - nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET, + nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len, + wowlan->patterns[i].pattern) || + nla_put_u32(msg, NL80211_PKTPAT_OFFSET, wowlan->patterns[i].pkt_offset)) return -ENOBUFS; nla_nest_end(msg, nl_pat); @@ -7937,7 +7936,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) struct nlattr *pat; int n_patterns = 0; int rem, pat_len, mask_len, pkt_offset; - struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT]; + struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], rem) @@ -7956,26 +7955,25 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], rem) { - nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT, - nla_data(pat), nla_len(pat), NULL); + nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), + nla_len(pat), NULL); err = -EINVAL; - if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] || - !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]) + if (!pat_tb[NL80211_PKTPAT_MASK] || + !pat_tb[NL80211_PKTPAT_PATTERN]) goto error; - pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]); + pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]); mask_len = DIV_ROUND_UP(pat_len, 8); - if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) != - mask_len) + if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len) goto error; if (pat_len > wowlan->pattern_max_len || pat_len < wowlan->pattern_min_len) goto error; - if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]) + if (!pat_tb[NL80211_PKTPAT_OFFSET]) pkt_offset = 0; else pkt_offset = nla_get_u32( - pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]); + pat_tb[NL80211_PKTPAT_OFFSET]); if (pkt_offset > wowlan->max_pkt_offset) goto error; new_triggers.patterns[i].pkt_offset = pkt_offset; @@ -7989,11 +7987,11 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) new_triggers.patterns[i].pattern = new_triggers.patterns[i].mask + mask_len; memcpy(new_triggers.patterns[i].mask, - nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]), + nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len); new_triggers.patterns[i].pattern_len = pat_len; memcpy(new_triggers.patterns[i].pattern, - nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]), + nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len); i++; } -- cgit v1.2.3-71-gd317 From 803768f54ef84cb4aaac0b51274b11b31885588c Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Fri, 28 Jun 2013 10:39:58 +0200 Subject: nl80211: enable HT overrides for ibss Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 5 +++++ net/wireless/nl80211.c | 13 +++++++++++++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 207b079a8fa5..79c2277e7c66 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1592,6 +1592,9 @@ struct cfg80211_disassoc_request { * user space. Otherwise, port is marked authorized by default. * @basic_rates: bitmap of basic rates to use when creating the IBSS * @mcast_rate: per-band multicast rate index + 1 (0: disabled) + * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask + * will be used in ht_capa. Un-supported values will be ignored. + * @ht_capa_mask: The bits of ht_capa which are to be used. */ struct cfg80211_ibss_params { u8 *ssid; @@ -1605,6 +1608,8 @@ struct cfg80211_ibss_params { bool privacy; bool control_port; int mcast_rate[IEEE80211_NUM_BANDS]; + struct ieee80211_ht_cap ht_capa; + struct ieee80211_ht_cap ht_capa_mask; }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a044762f5ea3..0492478ab74e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6346,6 +6346,19 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) return err; } + if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) + memcpy(&ibss.ht_capa_mask, + nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]), + sizeof(ibss.ht_capa_mask)); + + if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) { + if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) + return -EINVAL; + memcpy(&ibss.ht_capa, + nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]), + sizeof(ibss.ht_capa)); + } + if (info->attrs[NL80211_ATTR_MCAST_RATE] && !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate, nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) -- cgit v1.2.3-71-gd317 From ad24b0da9ecec433091f74596843703e4cf5da77 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 5 Jul 2013 11:53:28 +0200 Subject: wireless: indent kernel-doc with tabs Almost everywhere tabs are used to indent continuation lines, replace the few places that use spaces. Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 16 ++++++++-------- include/net/mac80211.h | 24 ++++++++++++------------ 2 files changed, 20 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 79c2277e7c66..49409602fe3d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -490,7 +490,7 @@ enum survey_info_flags { * @channel: the channel this survey record reports, mandatory * @filled: bitflag of flags from &enum survey_info_flags * @noise: channel noise in dBm. This and all following fields are - * optional + * optional * @channel_time: amount of time in ms the radio spent on the channel * @channel_time_busy: amount of time the primary channel was sensed busy * @channel_time_ext_busy: amount of time the extension channel was sensed busy @@ -546,9 +546,9 @@ struct cfg80211_crypto_settings { /** * struct cfg80211_beacon_data - beacon data * @head: head portion of beacon (before TIM IE) - * or %NULL if not changed + * or %NULL if not changed * @tail: tail portion of beacon (after TIM IE) - * or %NULL if not changed + * or %NULL if not changed * @head_len: length of @head * @tail_len: length of @tail * @beacon_ies: extra information element(s) to add into Beacon frames or %NULL @@ -764,7 +764,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy, * @STATION_INFO_PLINK_STATE: @plink_state filled * @STATION_INFO_SIGNAL: @signal filled * @STATION_INFO_TX_BITRATE: @txrate fields are filled - * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) + * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) * @STATION_INFO_RX_PACKETS: @rx_packets filled with 32-bit value * @STATION_INFO_TX_PACKETS: @tx_packets filled with 32-bit value * @STATION_INFO_TX_RETRIES: @tx_retries filled @@ -1509,7 +1509,7 @@ enum cfg80211_assoc_req_flags { * @prev_bssid: previous BSSID, if not %NULL use reassociate frame * @flags: See &enum cfg80211_assoc_req_flags * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask - * will be used in ht_capa. Un-supported values will be ignored. + * will be used in ht_capa. Un-supported values will be ignored. * @ht_capa_mask: The bits of ht_capa which are to be used. * @vht_capa: VHT capability override * @vht_capa_mask: VHT capability mask indicating which fields to use @@ -1593,7 +1593,7 @@ struct cfg80211_disassoc_request { * @basic_rates: bitmap of basic rates to use when creating the IBSS * @mcast_rate: per-band multicast rate index + 1 (0: disabled) * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask - * will be used in ht_capa. Un-supported values will be ignored. + * will be used in ht_capa. Un-supported values will be ignored. * @ht_capa_mask: The bits of ht_capa which are to be used. */ struct cfg80211_ibss_params { @@ -1635,9 +1635,9 @@ struct cfg80211_ibss_params { * @key: WEP key for shared key authentication * @flags: See &enum cfg80211_assoc_req_flags * @bg_scan_period: Background scan period in seconds - * or -1 to indicate that default value is to be used. + * or -1 to indicate that default value is to be used. * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask - * will be used in ht_capa. Un-supported values will be ignored. + * will be used in ht_capa. Un-supported values will be ignored. * @ht_capa_mask: The bits of ht_capa which are to be used. * @vht_capa: VHT Capability overrides * @vht_capa_mask: The bits of vht_capa which are to be used. diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 5b7a3dadadde..7c5dc787a8e8 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1004,11 +1004,11 @@ enum ieee80211_smps_mode { * @radar_enabled: whether radar detection is enabled * * @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame - * (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11, - * but actually means the number of transmissions not the number of retries + * (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11, + * but actually means the number of transmissions not the number of retries * @short_frame_max_tx_count: Maximum number of transmissions for a "short" - * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the - * number of transmissions not the number of retries + * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the + * number of transmissions not the number of retries * * @smps_mode: spatial multiplexing powersave mode; note that * %IEEE80211_SMPS_STATIC is used when the device is not @@ -1092,7 +1092,7 @@ enum ieee80211_vif_flags { * be off when it is %NULL there can still be races and packets could be * processed after it switches back to %NULL. * @debugfs_dir: debugfs dentry, can be used by drivers to create own per - * interface debug files. Note that it will be NULL for the virtual + * interface debug files. Note that it will be NULL for the virtual * monitor interface (if that is requested.) * @drv_priv: data area for driver use, will always be aligned to * sizeof(void *). @@ -1425,10 +1425,10 @@ struct ieee80211_tx_control { * the stack. * * @IEEE80211_HW_CONNECTION_MONITOR: - * The hardware performs its own connection monitoring, including - * periodic keep-alives to the AP and probing the AP on beacon loss. - * When this flag is set, signaling beacon-loss will cause an immediate - * change to disassociated state. + * The hardware performs its own connection monitoring, including + * periodic keep-alives to the AP and probing the AP on beacon loss. + * When this flag is set, signaling beacon-loss will cause an immediate + * change to disassociated state. * * @IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC: * This device needs to get data from beacon before association (i.e. @@ -1526,10 +1526,10 @@ enum ieee80211_hw_flags { * @channel_change_time: time (in microseconds) it takes to change channels. * * @max_signal: Maximum value for signal (rssi) in RX information, used - * only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB + * only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB * * @max_listen_interval: max listen interval in units of beacon interval - * that HW supports + * that HW supports * * @queues: number of available hardware transmit queues for * data packets. WMM/QoS requires at least four, these @@ -2443,7 +2443,7 @@ enum ieee80211_roc_type { * The callback can sleep. * * @set_tsf: Set the TSF timer to the specified value in the firmware/hardware. - * Currently, this is only used for IBSS mode debugging. Is not a + * Currently, this is only used for IBSS mode debugging. Is not a * required function. * The callback can sleep. * -- cgit v1.2.3-71-gd317 From be29b99a9b51b0338eea3c66a58de53bbd01de24 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 28 Jun 2013 11:51:26 -0700 Subject: cfg80211/nl80211: Add packet coalesce support In most cases, host that receives IPv4 and IPv6 multicast/broadcast packets does not do anything with these packets. Therefore the reception of these unwanted packets causes unnecessary processing and power consumption. Packet coalesce feature helps to reduce number of received interrupts to host by buffering these packets in firmware/hardware for some predefined time. Received interrupt will be generated when one of the following events occur. a) Expiration of hardware timer whose expiration time is set to maximum coalescing delay of matching coalesce rule. b) Coalescing buffer in hardware reaches it's limit. c) Packet doesn't match any of the configured coalesce rules. This patch adds set/get configuration support for packet coalesce. User needs to configure following parameters for creating a coalesce rule. a) Maximum coalescing delay b) List of packet patterns which needs to be matched c) Condition for coalescence. pattern 'match' or 'no match' Multiple such rules can be created. This feature needs to be advertised during driver initialization. Drivers are supposed to do required firmware/hardware settings based on user configuration. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao [fix kernel-doc, change free function, fix copy/paste error] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 54 ++++++++ include/uapi/linux/nl80211.h | 90 ++++++++++++- net/wireless/core.c | 9 ++ net/wireless/core.h | 2 + net/wireless/nl80211.c | 308 +++++++++++++++++++++++++++++++++++++++++++ net/wireless/nl80211.h | 2 + 6 files changed, 463 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 49409602fe3d..071ed2395c9a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1780,6 +1780,35 @@ struct cfg80211_wowlan { int n_patterns; }; +/** + * struct cfg80211_coalesce_rules - Coalesce rule parameters + * + * This structure defines coalesce rule for the device. + * @delay: maximum coalescing delay in msecs. + * @condition: condition for packet coalescence. + * see &enum nl80211_coalesce_condition. + * @patterns: array of packet patterns + * @n_patterns: number of patterns + */ +struct cfg80211_coalesce_rules { + int delay; + enum nl80211_coalesce_condition condition; + struct cfg80211_pkt_pattern *patterns; + int n_patterns; +}; + +/** + * struct cfg80211_coalesce - Packet coalescing settings + * + * This structure defines coalescing settings. + * @rules: array of coalesce rules + * @n_rules: number of rules + */ +struct cfg80211_coalesce { + struct cfg80211_coalesce_rules *rules; + int n_rules; +}; + /** * struct cfg80211_wowlan_wakeup - wakeup report * @disconnect: woke up by getting disconnected @@ -2076,6 +2105,7 @@ struct cfg80211_update_ft_ies_params { * driver can take the most appropriate actions. * @crit_proto_stop: Indicates critical protocol no longer needs increased link * reliability. This operation can not fail. + * @set_coalesce: Set coalesce parameters. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -2311,6 +2341,8 @@ struct cfg80211_ops { u16 duration); void (*crit_proto_stop)(struct wiphy *wiphy, struct wireless_dev *wdev); + int (*set_coalesce)(struct wiphy *wiphy, + struct cfg80211_coalesce *coalesce); }; /* @@ -2536,6 +2568,25 @@ struct wiphy_wowlan_support { const struct wiphy_wowlan_tcp_support *tcp; }; +/** + * struct wiphy_coalesce_support - coalesce support data + * @n_rules: maximum number of coalesce rules + * @max_delay: maximum supported coalescing delay in msecs + * @n_patterns: number of supported patterns in a rule + * (see nl80211.h for the pattern definition) + * @pattern_max_len: maximum length of each pattern + * @pattern_min_len: minimum length of each pattern + * @max_pkt_offset: maximum Rx packet offset + */ +struct wiphy_coalesce_support { + int n_rules; + int max_delay; + int n_patterns; + int pattern_max_len; + int pattern_min_len; + int max_pkt_offset; +}; + /** * struct wiphy - wireless hardware description * @reg_notifier: the driver's regulatory notification callback, @@ -2646,6 +2697,7 @@ struct wiphy_wowlan_support { * 802.11-2012 8.4.2.29 for the defined fields. * @extended_capabilities_mask: mask of the valid values * @extended_capabilities_len: length of the extended capabilities + * @coalesce: packet coalescing support information */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -2755,6 +2807,8 @@ struct wiphy { const struct iw_handler_def *wext; #endif + const struct wiphy_coalesce_support *coalesce; + char priv[0] __aligned(NETDEV_ALIGN); }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index de0ce809068a..5abc54d14d4d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -125,6 +125,31 @@ * interfaces that a given device supports. */ +/** + * DOC: packet coalesce support + * + * In most cases, host that receives IPv4 and IPv6 multicast/broadcast + * packets does not do anything with these packets. Therefore the + * reception of these unwanted packets causes unnecessary processing + * and power consumption. + * + * Packet coalesce feature helps to reduce number of received interrupts + * to host by buffering these packets in firmware/hardware for some + * predefined time. Received interrupt will be generated when one of the + * following events occur. + * a) Expiration of hardware timer whose expiration time is set to maximum + * coalescing delay of matching coalesce rule. + * b) Coalescing buffer in hardware reaches it's limit. + * c) Packet doesn't match any of the configured coalesce rules. + * + * User needs to configure following parameters for creating a coalesce + * rule. + * a) Maximum coalescing delay + * b) List of packet patterns which needs to be matched + * c) Condition for coalescence. pattern 'match' or 'no match' + * Multiple such rules can be created. + */ + /** * enum nl80211_commands - supported nl80211 commands * @@ -648,6 +673,9 @@ * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can * return back to normal. * + * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules. + * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -810,6 +838,9 @@ enum nl80211_commands { NL80211_CMD_CRIT_PROTOCOL_START, NL80211_CMD_CRIT_PROTOCOL_STOP, + NL80211_CMD_GET_COALESCE, + NL80211_CMD_SET_COALESCE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1436,6 +1467,8 @@ enum nl80211_commands { * allowed to be used with the first @NL80211_CMD_SET_STATION command to * update a TDLS peer STA entry. * + * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1736,6 +1769,8 @@ enum nl80211_attrs { NL80211_ATTR_PEER_AID, + NL80211_ATTR_COALESCE_RULE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3098,8 +3133,10 @@ enum nl80211_packet_pattern_attr { * @max_pkt_offset: maximum Rx packet offset * * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when - * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the - * capability information given by the kernel to userspace. + * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in + * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of + * %NL80211_ATTR_COALESCE_RULE in the capability information given + * by the kernel to userspace. */ struct nl80211_pattern_support { __u32 max_patterns; @@ -3317,6 +3354,55 @@ enum nl80211_wowlan_tcp_attrs { MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1 }; +/** + * struct nl80211_coalesce_rule_support - coalesce rule support information + * @max_rules: maximum number of rules supported + * @pat: packet pattern support information + * @max_delay: maximum supported coalescing delay in msecs + * + * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the + * capability information given by the kernel to userspace. + */ +struct nl80211_coalesce_rule_support { + __u32 max_rules; + struct nl80211_pattern_support pat; + __u32 max_delay; +} __attribute__((packed)); + +/** + * enum nl80211_attr_coalesce_rule - coalesce rule attribute + * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute + * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing + * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence, + * see &enum nl80211_coalesce_condition. + * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched + * after these fixed number of bytes of received packet + * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes + * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number + */ +enum nl80211_attr_coalesce_rule { + __NL80211_COALESCE_RULE_INVALID, + NL80211_ATTR_COALESCE_RULE_DELAY, + NL80211_ATTR_COALESCE_RULE_CONDITION, + NL80211_ATTR_COALESCE_RULE_PKT_PATTERN, + + /* keep last */ + NUM_NL80211_ATTR_COALESCE_RULE, + NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1 +}; + +/** + * enum nl80211_coalesce_condition - coalesce rule conditions + * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns + * in a rule are matched. + * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns + * in a rule are not matched. + */ +enum nl80211_coalesce_condition { + NL80211_COALESCE_CONDITION_MATCH, + NL80211_COALESCE_CONDITION_NO_MATCH +}; + /** * enum nl80211_iface_limit_attrs - limit attributes * @NL80211_IFACE_LIMIT_UNSPEC: (reserved) diff --git a/net/wireless/core.c b/net/wireless/core.c index 4f9f216665e9..389a3f2ee464 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -462,6 +462,14 @@ int wiphy_register(struct wiphy *wiphy) return -EINVAL; #endif + if (WARN_ON(wiphy->coalesce && + (!wiphy->coalesce->n_rules || + !wiphy->coalesce->n_patterns) && + (!wiphy->coalesce->pattern_min_len || + wiphy->coalesce->pattern_min_len > + wiphy->coalesce->pattern_max_len))) + return -EINVAL; + if (WARN_ON(wiphy->ap_sme_capa && !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) return -EINVAL; @@ -668,6 +676,7 @@ void wiphy_unregister(struct wiphy *wiphy) rdev_set_wakeup(rdev, false); #endif cfg80211_rdev_free_wowlan(rdev); + cfg80211_rdev_free_coalesce(rdev); } EXPORT_SYMBOL(wiphy_unregister); diff --git a/net/wireless/core.h b/net/wireless/core.h index a6b45bf00f33..9ad43c619c54 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -79,6 +79,8 @@ struct cfg80211_registered_device { /* netlink port which started critical protocol (0 means not started) */ u32 crit_proto_nlportid; + struct cfg80211_coalesce *coalesce; + /* must be last because of the way we do wiphy_priv(), * and it should at least be aligned to NETDEV_ALIGN */ struct wiphy wiphy __aligned(NETDEV_ALIGN); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 0492478ab74e..6dca5a700174 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -403,6 +403,14 @@ nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = { [NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 }, }; +/* policy for coalesce rule attributes */ +static const struct nla_policy +nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = { + [NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 }, + [NL80211_ATTR_COALESCE_RULE_CONDITION] = { .type = NLA_U32 }, + [NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED }, +}; + /* policy for GTK rekey offload attributes */ static const struct nla_policy nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { @@ -995,6 +1003,27 @@ static int nl80211_send_wowlan(struct sk_buff *msg, } #endif +static int nl80211_send_coalesce(struct sk_buff *msg, + struct cfg80211_registered_device *dev) +{ + struct nl80211_coalesce_rule_support rule; + + if (!dev->wiphy.coalesce) + return 0; + + rule.max_rules = dev->wiphy.coalesce->n_rules; + rule.max_delay = dev->wiphy.coalesce->max_delay; + rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns; + rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len; + rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len; + rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset; + + if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule)) + return -ENOBUFS; + + return 0; +} + static int nl80211_send_band_rateinfo(struct sk_buff *msg, struct ieee80211_supported_band *sband) { @@ -1513,6 +1542,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, dev->wiphy.vht_capa_mod_mask)) goto nla_put_failure; + state->split_start++; + break; + case 10: + if (nl80211_send_coalesce(msg, dev)) + goto nla_put_failure; + /* done */ state->split_start = 0; break; @@ -8043,6 +8078,264 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) } #endif +static int nl80211_send_coalesce_rules(struct sk_buff *msg, + struct cfg80211_registered_device *rdev) +{ + struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules; + int i, j, pat_len; + struct cfg80211_coalesce_rules *rule; + + if (!rdev->coalesce->n_rules) + return 0; + + nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE); + if (!nl_rules) + return -ENOBUFS; + + for (i = 0; i < rdev->coalesce->n_rules; i++) { + nl_rule = nla_nest_start(msg, i + 1); + if (!nl_rule) + return -ENOBUFS; + + rule = &rdev->coalesce->rules[i]; + if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY, + rule->delay)) + return -ENOBUFS; + + if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION, + rule->condition)) + return -ENOBUFS; + + nl_pats = nla_nest_start(msg, + NL80211_ATTR_COALESCE_RULE_PKT_PATTERN); + if (!nl_pats) + return -ENOBUFS; + + for (j = 0; j < rule->n_patterns; j++) { + nl_pat = nla_nest_start(msg, j + 1); + if (!nl_pat) + return -ENOBUFS; + pat_len = rule->patterns[j].pattern_len; + if (nla_put(msg, NL80211_PKTPAT_MASK, + DIV_ROUND_UP(pat_len, 8), + rule->patterns[j].mask) || + nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len, + rule->patterns[j].pattern) || + nla_put_u32(msg, NL80211_PKTPAT_OFFSET, + rule->patterns[j].pkt_offset)) + return -ENOBUFS; + nla_nest_end(msg, nl_pat); + } + nla_nest_end(msg, nl_pats); + nla_nest_end(msg, nl_rule); + } + nla_nest_end(msg, nl_rules); + + return 0; +} + +static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct sk_buff *msg; + void *hdr; + + if (!rdev->wiphy.coalesce) + return -EOPNOTSUPP; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0, + NL80211_CMD_GET_COALESCE); + if (!hdr) + goto nla_put_failure; + + if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + return genlmsg_reply(msg, info); + +nla_put_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + +void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev) +{ + struct cfg80211_coalesce *coalesce = rdev->coalesce; + int i, j; + struct cfg80211_coalesce_rules *rule; + + if (!coalesce) + return; + + for (i = 0; i < coalesce->n_rules; i++) { + rule = &coalesce->rules[i]; + for (j = 0; j < rule->n_patterns; j++) + kfree(rule->patterns[j].mask); + kfree(rule->patterns); + } + kfree(coalesce->rules); + kfree(coalesce); + rdev->coalesce = NULL; +} + +static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, + struct nlattr *rule, + struct cfg80211_coalesce_rules *new_rule) +{ + int err, i; + const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce; + struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat; + int rem, pat_len, mask_len, pkt_offset, n_patterns = 0; + struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; + + err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule), + nla_len(rule), nl80211_coalesce_policy); + if (err) + return err; + + if (tb[NL80211_ATTR_COALESCE_RULE_DELAY]) + new_rule->delay = + nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]); + if (new_rule->delay > coalesce->max_delay) + return -EINVAL; + + if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION]) + new_rule->condition = + nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]); + if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH && + new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH) + return -EINVAL; + + if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN]) + return -EINVAL; + + nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], + rem) + n_patterns++; + if (n_patterns > coalesce->n_patterns) + return -EINVAL; + + new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]), + GFP_KERNEL); + if (!new_rule->patterns) + return -ENOMEM; + + new_rule->n_patterns = n_patterns; + i = 0; + + nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], + rem) { + nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat), + nla_len(pat), NULL); + if (!pat_tb[NL80211_PKTPAT_MASK] || + !pat_tb[NL80211_PKTPAT_PATTERN]) + return -EINVAL; + pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]); + mask_len = DIV_ROUND_UP(pat_len, 8); + if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len) + return -EINVAL; + if (pat_len > coalesce->pattern_max_len || + pat_len < coalesce->pattern_min_len) + return -EINVAL; + + if (!pat_tb[NL80211_PKTPAT_OFFSET]) + pkt_offset = 0; + else + pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]); + if (pkt_offset > coalesce->max_pkt_offset) + return -EINVAL; + new_rule->patterns[i].pkt_offset = pkt_offset; + + new_rule->patterns[i].mask = + kmalloc(mask_len + pat_len, GFP_KERNEL); + if (!new_rule->patterns[i].mask) + return -ENOMEM; + new_rule->patterns[i].pattern = + new_rule->patterns[i].mask + mask_len; + memcpy(new_rule->patterns[i].mask, + nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len); + new_rule->patterns[i].pattern_len = pat_len; + memcpy(new_rule->patterns[i].pattern, + nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len); + i++; + } + + return 0; +} + +static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce; + struct cfg80211_coalesce new_coalesce = {}; + struct cfg80211_coalesce *n_coalesce; + int err, rem_rule, n_rules = 0, i, j; + struct nlattr *rule; + struct cfg80211_coalesce_rules *tmp_rule; + + if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) { + cfg80211_rdev_free_coalesce(rdev); + rdev->ops->set_coalesce(&rdev->wiphy, NULL); + return 0; + } + + nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE], + rem_rule) + n_rules++; + if (n_rules > coalesce->n_rules) + return -EINVAL; + + new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]), + GFP_KERNEL); + if (!new_coalesce.rules) + return -ENOMEM; + + new_coalesce.n_rules = n_rules; + i = 0; + + nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE], + rem_rule) { + err = nl80211_parse_coalesce_rule(rdev, rule, + &new_coalesce.rules[i]); + if (err) + goto error; + + i++; + } + + err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce); + if (err) + goto error; + + n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL); + if (!n_coalesce) { + err = -ENOMEM; + goto error; + } + cfg80211_rdev_free_coalesce(rdev); + rdev->coalesce = n_coalesce; + + return 0; +error: + for (i = 0; i < new_coalesce.n_rules; i++) { + tmp_rule = &new_coalesce.rules[i]; + for (j = 0; j < tmp_rule->n_patterns; j++) + kfree(tmp_rule->patterns[j].mask); + kfree(tmp_rule->patterns); + } + kfree(new_coalesce.rules); + + return err; +} + static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -9050,6 +9343,21 @@ static struct genl_ops nl80211_ops[] = { .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_GET_COALESCE, + .doit = nl80211_get_coalesce, + .policy = nl80211_policy, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_SET_COALESCE, + .doit = nl80211_set_coalesce, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, } }; diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index a4073e808c13..44341bf53cfc 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -74,4 +74,6 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev, enum nl80211_radar_event event, struct net_device *netdev, gfp_t gfp); +void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev); + #endif /* __NET_WIRELESS_NL80211_H */ -- cgit v1.2.3-71-gd317 From dcd6eac1f3b5fa1df11dfa99da0cf75b76cfef97 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Mon, 8 Jul 2013 16:55:49 +0200 Subject: nl80211: add scan width to bss and scan request structs To allow scanning and working with 5 MHz and 10 MHz BSS, extend the inform bss commands and add wrappers to take 5 and 10 MHz bss into account. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 54 +++++++++++++++++++++++++++++++++++++++++--- include/uapi/linux/nl80211.h | 18 +++++++++++++++ net/wireless/nl80211.c | 1 + net/wireless/scan.c | 31 +++++++++++++++---------- net/wireless/trace.h | 12 ++++++---- 5 files changed, 97 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 071ed2395c9a..bae8614a450f 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1285,6 +1285,7 @@ struct cfg80211_ssid { * @n_ssids: number of SSIDs * @channels: channels to scan on. * @n_channels: total number of channels to scan + * @scan_width: channel width for scanning * @ie: optional information element(s) to add into Probe Request or %NULL * @ie_len: length of ie in octets * @flags: bit field of flags controlling operation @@ -1300,6 +1301,7 @@ struct cfg80211_scan_request { struct cfg80211_ssid *ssids; int n_ssids; u32 n_channels; + enum nl80211_bss_scan_width scan_width; const u8 *ie; size_t ie_len; u32 flags; @@ -1333,6 +1335,7 @@ struct cfg80211_match_set { * @ssids: SSIDs to scan for (passed in the probe_reqs in active scans) * @n_ssids: number of SSIDs * @n_channels: total number of channels to scan + * @scan_width: channel width for scanning * @interval: interval between each scheduled scan cycle * @ie: optional information element(s) to add into Probe Request or %NULL * @ie_len: length of ie in octets @@ -1352,6 +1355,7 @@ struct cfg80211_sched_scan_request { struct cfg80211_ssid *ssids; int n_ssids; u32 n_channels; + enum nl80211_bss_scan_width scan_width; u32 interval; const u8 *ie; size_t ie_len; @@ -1403,6 +1407,7 @@ struct cfg80211_bss_ies { * for use in scan results and similar. * * @channel: channel this BSS is on + * @scan_width: width of the control channel * @bssid: BSSID of the BSS * @beacon_interval: the beacon interval as from the frame * @capability: the capability field in host byte order @@ -1424,6 +1429,7 @@ struct cfg80211_bss_ies { */ struct cfg80211_bss { struct ieee80211_channel *channel; + enum nl80211_bss_scan_width scan_width; const struct cfg80211_bss_ies __rcu *ies; const struct cfg80211_bss_ies __rcu *beacon_ies; @@ -3438,10 +3444,11 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy); void cfg80211_sched_scan_stopped(struct wiphy *wiphy); /** - * cfg80211_inform_bss_frame - inform cfg80211 of a received BSS frame + * cfg80211_inform_bss_width_frame - inform cfg80211 of a received BSS frame * * @wiphy: the wiphy reporting the BSS * @channel: The channel the frame was received on + * @scan_width: width of the control channel * @mgmt: the management frame (probe response or beacon) * @len: length of the management frame * @signal: the signal strength, type depends on the wiphy's signal_type @@ -3454,16 +3461,29 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy); * Or %NULL on error. */ struct cfg80211_bss * __must_check +cfg80211_inform_bss_width_frame(struct wiphy *wiphy, + struct ieee80211_channel *channel, + enum nl80211_bss_scan_width scan_width, + struct ieee80211_mgmt *mgmt, size_t len, + s32 signal, gfp_t gfp); + +static inline struct cfg80211_bss * __must_check cfg80211_inform_bss_frame(struct wiphy *wiphy, struct ieee80211_channel *channel, struct ieee80211_mgmt *mgmt, size_t len, - s32 signal, gfp_t gfp); + s32 signal, gfp_t gfp) +{ + return cfg80211_inform_bss_width_frame(wiphy, channel, + NL80211_BSS_CHAN_WIDTH_20, + mgmt, len, signal, gfp); +} /** * cfg80211_inform_bss - inform cfg80211 of a new BSS * * @wiphy: the wiphy reporting the BSS * @channel: The channel the frame was received on + * @scan_width: width of the control channel * @bssid: the BSSID of the BSS * @tsf: the TSF sent by the peer in the beacon/probe response (or 0) * @capability: the capability field sent by the peer @@ -3480,11 +3500,26 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, * Or %NULL on error. */ struct cfg80211_bss * __must_check +cfg80211_inform_bss_width(struct wiphy *wiphy, + struct ieee80211_channel *channel, + enum nl80211_bss_scan_width scan_width, + const u8 *bssid, u64 tsf, u16 capability, + u16 beacon_interval, const u8 *ie, size_t ielen, + s32 signal, gfp_t gfp); + +static inline struct cfg80211_bss * __must_check cfg80211_inform_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, u64 tsf, u16 capability, u16 beacon_interval, const u8 *ie, size_t ielen, - s32 signal, gfp_t gfp); + s32 signal, gfp_t gfp) +{ + return cfg80211_inform_bss_width(wiphy, channel, + NL80211_BSS_CHAN_WIDTH_20, + bssid, tsf, capability, + beacon_interval, ie, ielen, signal, + gfp); +} struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, @@ -3530,6 +3565,19 @@ void cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss); */ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss); +static inline enum nl80211_bss_scan_width +cfg80211_chandef_to_scan_width(const struct cfg80211_chan_def *chandef) +{ + switch (chandef->width) { + case NL80211_CHAN_WIDTH_5: + return NL80211_BSS_CHAN_WIDTH_5; + case NL80211_CHAN_WIDTH_10: + return NL80211_BSS_CHAN_WIDTH_10; + default: + return NL80211_BSS_CHAN_WIDTH_20; + } +} + /** * cfg80211_rx_mlme_mgmt - notification of processed MLME management frame * @dev: network device diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 5abc54d14d4d..eb68735b3318 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2807,6 +2807,21 @@ enum nl80211_chan_width { NL80211_CHAN_WIDTH_10, }; +/** + * enum nl80211_bss_scan_width - control channel width for a BSS + * + * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute. + * + * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible + * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide + * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide + */ +enum nl80211_bss_scan_width { + NL80211_BSS_CHAN_WIDTH_20, + NL80211_BSS_CHAN_WIDTH_10, + NL80211_BSS_CHAN_WIDTH_5, +}; + /** * enum nl80211_bss - netlink attributes for a BSS * @@ -2831,6 +2846,8 @@ enum nl80211_chan_width { * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information * elements from a Beacon frame (bin); not present if no Beacon frame has * yet been received + * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel + * (u32, enum nl80211_bss_scan_width) * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -2847,6 +2864,7 @@ enum nl80211_bss { NL80211_BSS_STATUS, NL80211_BSS_SEEN_MS_AGO, NL80211_BSS_BEACON_IES, + NL80211_BSS_CHAN_WIDTH, /* keep last */ __NL80211_BSS_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 6dca5a700174..ef4c312cc92c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5674,6 +5674,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, goto nla_put_failure; if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) || nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) || + nla_put_u32(msg, NL80211_BSS_CHAN_WIDTH, res->scan_width) || nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO, jiffies_to_msecs(jiffies - intbss->ts))) goto nla_put_failure; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index ae8c186b50d6..ad1e4068ce06 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -651,6 +651,8 @@ static bool cfg80211_combine_bsses(struct cfg80211_registered_device *dev, continue; if (bss->pub.channel != new->pub.channel) continue; + if (bss->pub.scan_width != new->pub.scan_width) + continue; if (rcu_access_pointer(bss->pub.beacon_ies)) continue; ies = rcu_access_pointer(bss->pub.ies); @@ -870,11 +872,12 @@ cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen, /* Returned bss is reference counted and must be cleaned up appropriately. */ struct cfg80211_bss* -cfg80211_inform_bss(struct wiphy *wiphy, - struct ieee80211_channel *channel, - const u8 *bssid, u64 tsf, u16 capability, - u16 beacon_interval, const u8 *ie, size_t ielen, - s32 signal, gfp_t gfp) +cfg80211_inform_bss_width(struct wiphy *wiphy, + struct ieee80211_channel *channel, + enum nl80211_bss_scan_width scan_width, + const u8 *bssid, u64 tsf, u16 capability, + u16 beacon_interval, const u8 *ie, size_t ielen, + s32 signal, gfp_t gfp) { struct cfg80211_bss_ies *ies; struct cfg80211_internal_bss tmp = {}, *res; @@ -892,6 +895,7 @@ cfg80211_inform_bss(struct wiphy *wiphy, memcpy(tmp.pub.bssid, bssid, ETH_ALEN); tmp.pub.channel = channel; + tmp.pub.scan_width = scan_width; tmp.pub.signal = signal; tmp.pub.beacon_interval = beacon_interval; tmp.pub.capability = capability; @@ -924,14 +928,15 @@ cfg80211_inform_bss(struct wiphy *wiphy, /* cfg80211_bss_update gives us a referenced result */ return &res->pub; } -EXPORT_SYMBOL(cfg80211_inform_bss); +EXPORT_SYMBOL(cfg80211_inform_bss_width); /* Returned bss is reference counted and must be cleaned up appropriately. */ struct cfg80211_bss * -cfg80211_inform_bss_frame(struct wiphy *wiphy, - struct ieee80211_channel *channel, - struct ieee80211_mgmt *mgmt, size_t len, - s32 signal, gfp_t gfp) +cfg80211_inform_bss_width_frame(struct wiphy *wiphy, + struct ieee80211_channel *channel, + enum nl80211_bss_scan_width scan_width, + struct ieee80211_mgmt *mgmt, size_t len, + s32 signal, gfp_t gfp) { struct cfg80211_internal_bss tmp = {}, *res; struct cfg80211_bss_ies *ies; @@ -941,7 +946,8 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) != offsetof(struct ieee80211_mgmt, u.beacon.variable)); - trace_cfg80211_inform_bss_frame(wiphy, channel, mgmt, len, signal); + trace_cfg80211_inform_bss_width_frame(wiphy, channel, scan_width, mgmt, + len, signal); if (WARN_ON(!mgmt)) return NULL; @@ -976,6 +982,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN); tmp.pub.channel = channel; + tmp.pub.scan_width = scan_width; tmp.pub.signal = signal; tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); @@ -991,7 +998,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, /* cfg80211_bss_update gives us a referenced result */ return &res->pub; } -EXPORT_SYMBOL(cfg80211_inform_bss_frame); +EXPORT_SYMBOL(cfg80211_inform_bss_width_frame); void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) { diff --git a/net/wireless/trace.h b/net/wireless/trace.h index e1534baf2ebb..09af6eb426a8 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -2391,26 +2391,30 @@ TRACE_EVENT(cfg80211_get_bss, __entry->capa_mask, __entry->capa_val) ); -TRACE_EVENT(cfg80211_inform_bss_frame, +TRACE_EVENT(cfg80211_inform_bss_width_frame, TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel, + enum nl80211_bss_scan_width scan_width, struct ieee80211_mgmt *mgmt, size_t len, s32 signal), - TP_ARGS(wiphy, channel, mgmt, len, signal), + TP_ARGS(wiphy, channel, scan_width, mgmt, len, signal), TP_STRUCT__entry( WIPHY_ENTRY CHAN_ENTRY + __field(enum nl80211_bss_scan_width, scan_width) __dynamic_array(u8, mgmt, len) __field(s32, signal) ), TP_fast_assign( WIPHY_ASSIGN; CHAN_ASSIGN(channel); + __entry->scan_width = scan_width; if (mgmt) memcpy(__get_dynamic_array(mgmt), mgmt, len); __entry->signal = signal; ), - TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "signal: %d", - WIPHY_PR_ARG, CHAN_PR_ARG, __entry->signal) + TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d", + WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width, + __entry->signal) ); DECLARE_EVENT_CLASS(cfg80211_bss_evt, -- cgit v1.2.3-71-gd317 From 3de805cf965d69c8d3d7d69368d5fd2c925a2d5a Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Mon, 8 Jul 2013 16:55:50 +0200 Subject: mac80211/rc80211: add chandef to rate initialization 5 and 10 MHz support needs to know the current operating channel width, add the chandef to the rate control API. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath9k/rc.c | 2 ++ drivers/net/wireless/iwlegacy/3945-rs.c | 1 + drivers/net/wireless/iwlegacy/4965-rs.c | 1 + drivers/net/wireless/iwlwifi/dvm/rs.c | 3 ++- drivers/net/wireless/iwlwifi/mvm/rs.c | 5 +++-- drivers/net/wireless/rtlwifi/rc.c | 1 + include/net/mac80211.h | 2 ++ net/mac80211/rate.h | 22 +++++++++++++++++----- net/mac80211/rc80211_minstrel.c | 3 ++- net/mac80211/rc80211_minstrel_ht.c | 10 +++++++--- net/mac80211/rc80211_pid_algo.c | 1 + 11 files changed, 39 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 7eb1f4b458e4..a3c4ca0c94bf 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1275,6 +1275,7 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, } static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *priv_sta) { struct ath_softc *sc = priv; @@ -1313,6 +1314,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, } static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *priv_sta, u32 changed) { diff --git a/drivers/net/wireless/iwlegacy/3945-rs.c b/drivers/net/wireless/iwlegacy/3945-rs.c index fe31590a51b2..aea667b430c3 100644 --- a/drivers/net/wireless/iwlegacy/3945-rs.c +++ b/drivers/net/wireless/iwlegacy/3945-rs.c @@ -887,6 +887,7 @@ il3945_remove_debugfs(void *il, void *il_sta) */ static void il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *il_sta) { } diff --git a/drivers/net/wireless/iwlegacy/4965-rs.c b/drivers/net/wireless/iwlegacy/4965-rs.c index ed3c42a63a43..3ccbaf791b48 100644 --- a/drivers/net/wireless/iwlegacy/4965-rs.c +++ b/drivers/net/wireless/iwlegacy/4965-rs.c @@ -2803,6 +2803,7 @@ il4965_rs_remove_debugfs(void *il, void *il_sta) */ static void il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *il_sta) { } diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index 1b693944123b..91eb09b9b56f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -3319,7 +3319,8 @@ static void rs_remove_debugfs(void *priv, void *priv_sta) * station is added we ignore it. */ static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta) + struct cfg80211_chan_def *chandef, + struct ieee80211_sta *sta, void *priv_sta) { } static struct rate_control_ops rs_ops = { diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index b328a988c130..376ea2112de2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -3159,8 +3159,9 @@ static void rs_remove_debugfs(void *mvm, void *mvm_sta) * station is added we ignore it. */ static void rs_rate_init_stub(void *mvm_r, - struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *mvm_sta) + struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, + struct ieee80211_sta *sta, void *mvm_sta) { } static struct rate_control_ops rs_mvm_ops = { diff --git a/drivers/net/wireless/rtlwifi/rc.c b/drivers/net/wireless/rtlwifi/rc.c index f9f059dadb73..a98acefb8c06 100644 --- a/drivers/net/wireless/rtlwifi/rc.c +++ b/drivers/net/wireless/rtlwifi/rc.c @@ -218,6 +218,7 @@ static void rtl_tx_status(void *ppriv, static void rtl_rate_init(void *ppriv, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *priv_sta) { } diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7c5dc787a8e8..fe5fee830aa8 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4204,8 +4204,10 @@ struct rate_control_ops { void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp); void (*rate_init)(void *priv, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *priv_sta); void (*rate_update)(void *priv, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *priv_sta, u32 changed); void (*free_sta)(void *priv, struct ieee80211_sta *sta, diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index d35a5dd3fb13..5dedc56c94db 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -66,11 +66,12 @@ static inline void rate_control_rate_init(struct sta_info *sta) } sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band]; - rcu_read_unlock(); ieee80211_sta_set_rx_nss(sta); - ref->ops->rate_init(ref->priv, sband, ista, priv_sta); + ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista, + priv_sta); + rcu_read_unlock(); set_sta_flag(sta, WLAN_STA_RATE_CONTROL); } @@ -81,10 +82,21 @@ static inline void rate_control_rate_update(struct ieee80211_local *local, struct rate_control_ref *ref = local->rate_ctrl; struct ieee80211_sta *ista = &sta->sta; void *priv_sta = sta->rate_ctrl_priv; + struct ieee80211_chanctx_conf *chanctx_conf; + + if (ref && ref->ops->rate_update) { + rcu_read_lock(); - if (ref && ref->ops->rate_update) - ref->ops->rate_update(ref->priv, sband, ista, - priv_sta, changed); + chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf); + if (WARN_ON(!chanctx_conf)) { + rcu_read_unlock(); + return; + } + + ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def, + ista, priv_sta, changed); + rcu_read_unlock(); + } drv_sta_rc_update(local, sta->sdata, &sta->sta, changed); } diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index ac7ef5414bde..5b25966add10 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -417,7 +417,8 @@ init_sample_table(struct minstrel_sta_info *mi) static void minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta) + struct cfg80211_chan_def *chandef, + struct ieee80211_sta *sta, void *priv_sta) { struct minstrel_sta_info *mi = priv_sta; struct minstrel_priv *mp = priv; diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 5b2d3012b983..52562973fbd1 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -836,6 +836,7 @@ minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, static void minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *priv_sta) { struct minstrel_priv *mp = priv; @@ -931,22 +932,25 @@ use_legacy: memset(&msp->legacy, 0, sizeof(msp->legacy)); msp->legacy.r = msp->ratelist; msp->legacy.sample_table = msp->sample_table; - return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy); + return mac80211_minstrel.rate_init(priv, sband, chandef, sta, + &msp->legacy); } static void minstrel_ht_rate_init(void *priv, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *priv_sta) { - minstrel_ht_update_caps(priv, sband, sta, priv_sta); + minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta); } static void minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *priv_sta, u32 changed) { - minstrel_ht_update_caps(priv, sband, sta, priv_sta); + minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta); } static void * diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index 502d3ecc4a79..958fad07b54c 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -293,6 +293,7 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta, static void rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, struct ieee80211_sta *sta, void *priv_sta) { struct rc_pid_sta_info *spinfo = priv_sta; -- cgit v1.2.3-71-gd317 From a5e70697d0c4836e69c60de92db27eac9ae71e05 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Mon, 8 Jul 2013 16:55:52 +0200 Subject: mac80211: add radiotap flag and handling for 5/10 MHz Wireshark already defines radiotap channel flags for 5 and 10 MHz, so just use them in Linux radiotap too. Furthermore, add rx status flags to allow drivers to report when they received data on 5 or 10 MHz channels. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: Johannes Berg --- include/net/ieee80211_radiotap.h | 4 ++++ include/net/mac80211.h | 4 ++++ net/mac80211/rx.c | 21 ++++++++++++--------- 3 files changed, 20 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index c6d07cb074bc..8b5b71433297 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -230,6 +230,10 @@ enum ieee80211_radiotap_type { #define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ #define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ #define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ +#define IEEE80211_CHAN_GSM 0x1000 /* GSM (900 MHz) */ +#define IEEE80211_CHAN_STURBO 0x2000 /* Static Turbo */ +#define IEEE80211_CHAN_HALF 0x4000 /* Half channel (10 MHz wide) */ +#define IEEE80211_CHAN_QUARTER 0x8000 /* Quarter channel (5 MHz wide) */ /* For IEEE80211_RADIOTAP_FLAGS */ #define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received diff --git a/include/net/mac80211.h b/include/net/mac80211.h index fe5fee830aa8..3124036285eb 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -811,6 +811,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC * is stored in the @ampdu_delimiter_crc field) * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3 + * @RX_FLAG_10MHZ: 10 MHz (half channel) was used + * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used */ enum mac80211_rx_flags { RX_FLAG_MMIC_ERROR = BIT(0), @@ -839,6 +841,8 @@ enum mac80211_rx_flags { RX_FLAG_80P80MHZ = BIT(24), RX_FLAG_160MHZ = BIT(25), RX_FLAG_STBC_MASK = BIT(26) | BIT(27), + RX_FLAG_10MHZ = BIT(28), + RX_FLAG_5MHZ = BIT(29), }; #define RX_FLAG_STBC_SHIFT 26 diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index c9f2ca43856a..deeeca1299bd 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -159,6 +159,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, __le32 *it_present; u32 it_present_val; u16 rx_flags = 0; + u16 channel_flags = 0; int mpdulen, chain; unsigned long chains = status->chains; @@ -243,20 +244,22 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* IEEE80211_RADIOTAP_CHANNEL */ put_unaligned_le16(status->freq, pos); pos += 2; + if (status->flag & RX_FLAG_10MHZ) + channel_flags |= IEEE80211_CHAN_HALF; + else if (status->flag & RX_FLAG_5MHZ) + channel_flags |= IEEE80211_CHAN_QUARTER; + if (status->band == IEEE80211_BAND_5GHZ) - put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ, - pos); + channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ; else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) - put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ, - pos); + channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; else if (rate && rate->flags & IEEE80211_RATE_ERP_G) - put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ, - pos); + channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ; else if (rate) - put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ, - pos); + channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ; else - put_unaligned_le16(IEEE80211_CHAN_2GHZ, pos); + channel_flags |= IEEE80211_CHAN_2GHZ; + put_unaligned_le16(channel_flags, pos); pos += 2; /* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */ -- cgit v1.2.3-71-gd317 From 74608aca4d82e2855b1a6a2cd60331d1a98f2d6a Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Mon, 8 Jul 2013 16:55:54 +0200 Subject: cfg80211/mac80211: get mandatory rates based on scan width Mandatory rates for 5 and 10 MHz are different from the rates used for 20 MHz in 2.4 GHz mode, as they use OFDM only. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 4 +++- net/mac80211/ibss.c | 19 +++++++++++++++---- net/wireless/mesh.c | 5 ++++- net/wireless/util.c | 14 ++++++++++---- 4 files changed, 32 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index bae8614a450f..88f15631701a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3128,11 +3128,13 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband, /** * ieee80211_mandatory_rates - get mandatory rates for a given band * @sband: the band to look for rates in + * @scan_width: width of the control channel * * This function returns a bitmap of the mandatory rates for the given * band, bits are set according to the rate position in the bitrates array. */ -u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband); +u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband, + enum nl80211_bss_scan_width scan_width); /* * Radiotap parsing functions -- for controlled injection support diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 272a3b37615c..299603e4939f 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -371,6 +371,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid, struct sta_info *sta; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_supported_band *sband; + enum nl80211_bss_scan_width scan_width; int band; /* @@ -399,6 +400,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid, if (WARN_ON_ONCE(!chanctx_conf)) return NULL; band = chanctx_conf->def.chan->band; + scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def); rcu_read_unlock(); sta = sta_info_alloc(sdata, addr, GFP_KERNEL); @@ -412,7 +414,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid, /* make sure mandatory rates are always added */ sband = local->hw.wiphy->bands[band]; sta->sta.supp_rates[band] = supp_rates | - ieee80211_mandatory_rates(sband); + ieee80211_mandatory_rates(sband, scan_width); return ieee80211_ibss_finish_sta(sta); } @@ -476,6 +478,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, u64 beacon_timestamp, rx_timestamp; u32 supp_rates = 0; enum ieee80211_band band = rx_status->band; + enum nl80211_bss_scan_width scan_width; struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; bool rates_updated = false; @@ -504,9 +507,15 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, prev_rates = sta->sta.supp_rates[band]; /* make sure mandatory rates are always added */ - sta->sta.supp_rates[band] = supp_rates | - ieee80211_mandatory_rates(sband); + scan_width = NL80211_BSS_CHAN_WIDTH_20; + if (rx_status->flag & RX_FLAG_5MHZ) + scan_width = NL80211_BSS_CHAN_WIDTH_5; + if (rx_status->flag & RX_FLAG_10MHZ) + scan_width = NL80211_BSS_CHAN_WIDTH_10; + sta->sta.supp_rates[band] = supp_rates | + ieee80211_mandatory_rates(sband, + scan_width); if (sta->sta.supp_rates[band] != prev_rates) { ibss_dbg(sdata, "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n", @@ -640,6 +649,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_supported_band *sband; + enum nl80211_bss_scan_width scan_width; int band; /* @@ -665,6 +675,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, return; } band = chanctx_conf->def.chan->band; + scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def); rcu_read_unlock(); sta = sta_info_alloc(sdata, addr, GFP_ATOMIC); @@ -676,7 +687,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, /* make sure mandatory rates are always added */ sband = local->hw.wiphy->bands[band]; sta->sta.supp_rates[band] = supp_rates | - ieee80211_mandatory_rates(sband); + ieee80211_mandatory_rates(sband, scan_width); spin_lock(&ifibss->incomplete_lock); list_add(&sta->list, &ifibss->incomplete_stations); diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 30c49202ee4d..0553fd4d85ae 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -167,9 +167,12 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, * basic rates */ if (!setup->basic_rates) { + enum nl80211_bss_scan_width scan_width; struct ieee80211_supported_band *sband = rdev->wiphy.bands[setup->chandef.chan->band]; - setup->basic_rates = ieee80211_mandatory_rates(sband); + scan_width = cfg80211_chandef_to_scan_width(&setup->chandef); + setup->basic_rates = ieee80211_mandatory_rates(sband, + scan_width); } if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef)) diff --git a/net/wireless/util.c b/net/wireless/util.c index 74458b7f61eb..ce090c1c5e4f 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -33,7 +33,8 @@ ieee80211_get_response_rate(struct ieee80211_supported_band *sband, } EXPORT_SYMBOL(ieee80211_get_response_rate); -u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband) +u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband, + enum nl80211_bss_scan_width scan_width) { struct ieee80211_rate *bitrates; u32 mandatory_rates = 0; @@ -43,10 +44,15 @@ u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband) if (WARN_ON(!sband)) return 1; - if (sband->band == IEEE80211_BAND_2GHZ) - mandatory_flag = IEEE80211_RATE_MANDATORY_B; - else + if (sband->band == IEEE80211_BAND_2GHZ) { + if (scan_width == NL80211_BSS_CHAN_WIDTH_5 || + scan_width == NL80211_BSS_CHAN_WIDTH_10) + mandatory_flag = IEEE80211_RATE_MANDATORY_G; + else + mandatory_flag = IEEE80211_RATE_MANDATORY_B; + } else { mandatory_flag = IEEE80211_RATE_MANDATORY_A; + } bitrates = sband->bitrates; for (i = 0; i < sband->n_bitrates; i++) -- cgit v1.2.3-71-gd317 From 0430c883470d0c9a23661ea9f02c56b1d91cf93c Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Mon, 8 Jul 2013 16:55:55 +0200 Subject: cfg80211/mac80211: use reduced txpower for 5 and 10 MHz Some regulations (like germany, but also FCC) express their transmission power limit in dBm/MHz or mW/MHz. To cope with that and be on the safe side, reduce the maximum power to half (10 MHz) or quarter (5 MHz) when operating on these reduced bandwidth channels. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 27 +++++++++++++++++++++++++++ net/mac80211/iface.c | 2 +- net/mac80211/main.c | 2 +- net/mac80211/mlme.c | 3 ++- 4 files changed, 31 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 88f15631701a..aeaf6dff6e05 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -460,6 +460,33 @@ ieee80211_chandef_rate_flags(struct cfg80211_chan_def *chandef) return 0; } +/** + * ieee80211_chandef_max_power - maximum transmission power for the chandef + * + * In some regulations, the transmit power may depend on the configured channel + * bandwidth which may be defined as dBm/MHz. This function returns the actual + * max_power for non-standard (20 MHz) channels. + * + * @chandef: channel definition for the channel + * + * Returns: maximum allowed transmission power in dBm for the chandef + */ +static inline int +ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef) +{ + switch (chandef->width) { + case NL80211_CHAN_WIDTH_5: + return min(chandef->chan->max_reg_power - 6, + chandef->chan->max_power); + case NL80211_CHAN_WIDTH_10: + return min(chandef->chan->max_reg_power - 3, + chandef->chan->max_power); + default: + break; + } + return chandef->chan->max_power; +} + /** * enum survey_info_flags - survey information flags * diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index cc117591f678..4c41c11958c8 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -54,7 +54,7 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata) return false; } - power = chanctx_conf->def.chan->max_power; + power = ieee80211_chandef_max_power(&chanctx_conf->def); rcu_read_unlock(); if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 091088ac7890..6da98c77496b 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -151,7 +151,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) changed |= IEEE80211_CONF_CHANGE_SMPS; } - power = chandef.chan->max_power; + power = ieee80211_chandef_max_power(&chandef); rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) { diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f7552c2b74eb..211246b46819 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -747,7 +747,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) *pos++ = WLAN_EID_PWR_CAPABILITY; *pos++ = 2; *pos++ = 0; /* min tx power */ - *pos++ = chan->max_power; /* max tx power */ + /* max tx power */ + *pos++ = ieee80211_chandef_max_power(&chanctx_conf->def); /* 2. supported channels */ /* TODO: get this in reg domain format */ -- cgit v1.2.3-71-gd317 From dc34e7e1a701134c2639dc7af78dc91055616477 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Mon, 1 Jul 2013 00:01:32 +0400 Subject: libata: move 'struct ata_taskfile' and friends from ata.h to libata.h Move 'struct ata_taskfile', ata_prot_flags() and their friends from to . They were misplaced from the beginning, as should cover ATA/ATAPI and related standards only -- to which the aforementioned structure and function have only remote relation. I would have moved 'enum ata_tf_protocols' closely related to 'struct ata_taskfile' but it unfortunately gets used by 'drivers/ide/ide-ioctls.c'... Signed-off-by: Sergei Shtylyov Signed-off-by: Tejun Heo --- include/linux/ata.h | 102 ------------------------------------------------- include/linux/libata.h | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 102 deletions(-) (limited to 'include') diff --git a/include/linux/ata.h b/include/linux/ata.h index ee0bd9524055..f63fb1afc5cc 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -446,22 +446,6 @@ enum { SERR_TRANS_ST_ERROR = (1 << 24), /* Transport state trans. error */ SERR_UNRECOG_FIS = (1 << 25), /* Unrecognized FIS */ SERR_DEV_XCHG = (1 << 26), /* device exchanged */ - - /* struct ata_taskfile flags */ - ATA_TFLAG_LBA48 = (1 << 0), /* enable 48-bit LBA and "HOB" */ - ATA_TFLAG_ISADDR = (1 << 1), /* enable r/w to nsect/lba regs */ - ATA_TFLAG_DEVICE = (1 << 2), /* enable r/w to device reg */ - ATA_TFLAG_WRITE = (1 << 3), /* data dir: host->dev==1 (write) */ - ATA_TFLAG_LBA = (1 << 4), /* enable LBA */ - ATA_TFLAG_FUA = (1 << 5), /* enable FUA */ - ATA_TFLAG_POLLING = (1 << 6), /* set nIEN to 1 and use polling */ - - /* protocol flags */ - ATA_PROT_FLAG_PIO = (1 << 0), /* is PIO */ - ATA_PROT_FLAG_DMA = (1 << 1), /* is DMA */ - ATA_PROT_FLAG_DATA = ATA_PROT_FLAG_PIO | ATA_PROT_FLAG_DMA, - ATA_PROT_FLAG_NCQ = (1 << 2), /* is NCQ */ - ATA_PROT_FLAG_ATAPI = (1 << 3), /* is ATAPI */ }; enum ata_tf_protocols { @@ -488,83 +472,6 @@ struct ata_bmdma_prd { __le32 flags_len; }; -struct ata_taskfile { - unsigned long flags; /* ATA_TFLAG_xxx */ - u8 protocol; /* ATA_PROT_xxx */ - - u8 ctl; /* control reg */ - - u8 hob_feature; /* additional data */ - u8 hob_nsect; /* to support LBA48 */ - u8 hob_lbal; - u8 hob_lbam; - u8 hob_lbah; - - u8 feature; - u8 nsect; - u8 lbal; - u8 lbam; - u8 lbah; - - u8 device; - - u8 command; /* IO operation */ -}; - -/* - * protocol tests - */ -static inline unsigned int ata_prot_flags(u8 prot) -{ - switch (prot) { - case ATA_PROT_NODATA: - return 0; - case ATA_PROT_PIO: - return ATA_PROT_FLAG_PIO; - case ATA_PROT_DMA: - return ATA_PROT_FLAG_DMA; - case ATA_PROT_NCQ: - return ATA_PROT_FLAG_DMA | ATA_PROT_FLAG_NCQ; - case ATAPI_PROT_NODATA: - return ATA_PROT_FLAG_ATAPI; - case ATAPI_PROT_PIO: - return ATA_PROT_FLAG_ATAPI | ATA_PROT_FLAG_PIO; - case ATAPI_PROT_DMA: - return ATA_PROT_FLAG_ATAPI | ATA_PROT_FLAG_DMA; - } - return 0; -} - -static inline int ata_is_atapi(u8 prot) -{ - return ata_prot_flags(prot) & ATA_PROT_FLAG_ATAPI; -} - -static inline int ata_is_nodata(u8 prot) -{ - return !(ata_prot_flags(prot) & ATA_PROT_FLAG_DATA); -} - -static inline int ata_is_pio(u8 prot) -{ - return ata_prot_flags(prot) & ATA_PROT_FLAG_PIO; -} - -static inline int ata_is_dma(u8 prot) -{ - return ata_prot_flags(prot) & ATA_PROT_FLAG_DMA; -} - -static inline int ata_is_ncq(u8 prot) -{ - return ata_prot_flags(prot) & ATA_PROT_FLAG_NCQ; -} - -static inline int ata_is_data(u8 prot) -{ - return ata_prot_flags(prot) & ATA_PROT_FLAG_DATA; -} - /* * id tests */ @@ -1060,15 +967,6 @@ static inline unsigned ata_set_lba_range_entries(void *_buffer, return used_bytes; } -static inline int is_multi_taskfile(struct ata_taskfile *tf) -{ - return (tf->command == ATA_CMD_READ_MULTI) || - (tf->command == ATA_CMD_WRITE_MULTI) || - (tf->command == ATA_CMD_READ_MULTI_EXT) || - (tf->command == ATA_CMD_WRITE_MULTI_EXT) || - (tf->command == ATA_CMD_WRITE_MULTI_FUA_EXT); -} - static inline bool ata_ok(u8 status) { return ((status & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ | ATA_ERR)) diff --git a/include/linux/libata.h b/include/linux/libata.h index 4ea55bb45deb..283d66bc603c 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -138,6 +138,22 @@ enum { ATA_SHT_THIS_ID = -1, ATA_SHT_USE_CLUSTERING = 1, + /* struct ata_taskfile flags */ + ATA_TFLAG_LBA48 = (1 << 0), /* enable 48-bit LBA and "HOB" */ + ATA_TFLAG_ISADDR = (1 << 1), /* enable r/w to nsect/lba regs */ + ATA_TFLAG_DEVICE = (1 << 2), /* enable r/w to device reg */ + ATA_TFLAG_WRITE = (1 << 3), /* data dir: host->dev==1 (write) */ + ATA_TFLAG_LBA = (1 << 4), /* enable LBA */ + ATA_TFLAG_FUA = (1 << 5), /* enable FUA */ + ATA_TFLAG_POLLING = (1 << 6), /* set nIEN to 1 and use polling */ + + /* protocol flags */ + ATA_PROT_FLAG_PIO = (1 << 0), /* is PIO */ + ATA_PROT_FLAG_DMA = (1 << 1), /* is DMA */ + ATA_PROT_FLAG_DATA = ATA_PROT_FLAG_PIO | ATA_PROT_FLAG_DMA, + ATA_PROT_FLAG_NCQ = (1 << 2), /* is NCQ */ + ATA_PROT_FLAG_ATAPI = (1 << 3), /* is ATAPI */ + /* struct ata_device stuff */ ATA_DFLAG_LBA = (1 << 0), /* device supports LBA */ ATA_DFLAG_LBA48 = (1 << 1), /* device supports LBA48 */ @@ -518,6 +534,29 @@ enum sw_activity { BLINK_OFF, }; +struct ata_taskfile { + unsigned long flags; /* ATA_TFLAG_xxx */ + u8 protocol; /* ATA_PROT_xxx */ + + u8 ctl; /* control reg */ + + u8 hob_feature; /* additional data */ + u8 hob_nsect; /* to support LBA48 */ + u8 hob_lbal; + u8 hob_lbam; + u8 hob_lbah; + + u8 feature; + u8 nsect; + u8 lbal; + u8 lbam; + u8 lbah; + + u8 device; + + u8 command; /* IO operation */ +}; + #ifdef CONFIG_ATA_SFF struct ata_ioports { void __iomem *cmd_addr; @@ -959,6 +998,69 @@ extern const unsigned long sata_deb_timing_long[]; extern struct ata_port_operations ata_dummy_port_ops; extern const struct ata_port_info ata_dummy_port_info; +/* + * protocol tests + */ +static inline unsigned int ata_prot_flags(u8 prot) +{ + switch (prot) { + case ATA_PROT_NODATA: + return 0; + case ATA_PROT_PIO: + return ATA_PROT_FLAG_PIO; + case ATA_PROT_DMA: + return ATA_PROT_FLAG_DMA; + case ATA_PROT_NCQ: + return ATA_PROT_FLAG_DMA | ATA_PROT_FLAG_NCQ; + case ATAPI_PROT_NODATA: + return ATA_PROT_FLAG_ATAPI; + case ATAPI_PROT_PIO: + return ATA_PROT_FLAG_ATAPI | ATA_PROT_FLAG_PIO; + case ATAPI_PROT_DMA: + return ATA_PROT_FLAG_ATAPI | ATA_PROT_FLAG_DMA; + } + return 0; +} + +static inline int ata_is_atapi(u8 prot) +{ + return ata_prot_flags(prot) & ATA_PROT_FLAG_ATAPI; +} + +static inline int ata_is_nodata(u8 prot) +{ + return !(ata_prot_flags(prot) & ATA_PROT_FLAG_DATA); +} + +static inline int ata_is_pio(u8 prot) +{ + return ata_prot_flags(prot) & ATA_PROT_FLAG_PIO; +} + +static inline int ata_is_dma(u8 prot) +{ + return ata_prot_flags(prot) & ATA_PROT_FLAG_DMA; +} + +static inline int ata_is_ncq(u8 prot) +{ + return ata_prot_flags(prot) & ATA_PROT_FLAG_NCQ; +} + +static inline int ata_is_data(u8 prot) +{ + return ata_prot_flags(prot) & ATA_PROT_FLAG_DATA; +} + +static inline int is_multi_taskfile(struct ata_taskfile *tf) +{ + return (tf->command == ATA_CMD_READ_MULTI) || + (tf->command == ATA_CMD_WRITE_MULTI) || + (tf->command == ATA_CMD_READ_MULTI_EXT) || + (tf->command == ATA_CMD_WRITE_MULTI_EXT) || + (tf->command == ATA_CMD_WRITE_MULTI_FUA_EXT); +} + static inline const unsigned long * sata_ehc_deb_timing(struct ata_eh_context *ehc) { -- cgit v1.2.3-71-gd317 From 55708698c5f153f4e390175cdfc395333b2eafbd Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Tue, 16 Jul 2013 17:56:12 +0800 Subject: fs/anon_inode: Introduce a new lib function anon_inode_getfile_private() Introduce a new lib function anon_inode_getfile_private(), it creates a new file instance by hooking it up to an anonymous inode, and a dentry that describe the "class" of the file, similar to anon_inode_getfile(), but each file holds a single inode. Furthermore, anyone who wants to create a private anon file will benefit from this change. Signed-off-by: Gu Zheng Signed-off-by: Benjamin LaHaise --- fs/anon_inodes.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/anon_inodes.h | 3 +++ 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c index 47a65df8c871..85c961849953 100644 --- a/fs/anon_inodes.c +++ b/fs/anon_inodes.c @@ -108,6 +108,72 @@ static struct file_system_type anon_inode_fs_type = { .kill_sb = kill_anon_super, }; +/** + * anon_inode_getfile_private - creates a new file instance by hooking it up to an + * anonymous inode, and a dentry that describe the "class" + * of the file + * + * @name: [in] name of the "class" of the new file + * @fops: [in] file operations for the new file + * @priv: [in] private data for the new file (will be file's private_data) + * @flags: [in] flags + * + * + * Similar to anon_inode_getfile, but each file holds a single inode. + * + */ +struct file *anon_inode_getfile_private(const char *name, + const struct file_operations *fops, + void *priv, int flags) +{ + struct qstr this; + struct path path; + struct file *file; + struct inode *inode; + + if (fops->owner && !try_module_get(fops->owner)) + return ERR_PTR(-ENOENT); + + inode = anon_inode_mkinode(anon_inode_mnt->mnt_sb); + if (IS_ERR(inode)) { + file = ERR_PTR(-ENOMEM); + goto err_module; + } + + /* + * Link the inode to a directory entry by creating a unique name + * using the inode sequence number. + */ + file = ERR_PTR(-ENOMEM); + this.name = name; + this.len = strlen(name); + this.hash = 0; + path.dentry = d_alloc_pseudo(anon_inode_mnt->mnt_sb, &this); + if (!path.dentry) + goto err_module; + + path.mnt = mntget(anon_inode_mnt); + + d_instantiate(path.dentry, inode); + + file = alloc_file(&path, OPEN_FMODE(flags), fops); + if (IS_ERR(file)) + goto err_dput; + + file->f_mapping = inode->i_mapping; + file->f_flags = flags & (O_ACCMODE | O_NONBLOCK); + file->private_data = priv; + + return file; + +err_dput: + path_put(&path); +err_module: + module_put(fops->owner); + return file; +} +EXPORT_SYMBOL_GPL(anon_inode_getfile_private); + /** * anon_inode_getfile - creates a new file instance by hooking it up to an * anonymous inode, and a dentry that describe the "class" diff --git a/include/linux/anon_inodes.h b/include/linux/anon_inodes.h index 8013a45242fe..cf573c22b81e 100644 --- a/include/linux/anon_inodes.h +++ b/include/linux/anon_inodes.h @@ -13,6 +13,9 @@ struct file_operations; struct file *anon_inode_getfile(const char *name, const struct file_operations *fops, void *priv, int flags); +struct file *anon_inode_getfile_private(const char *name, + const struct file_operations *fops, + void *priv, int flags); int anon_inode_getfd(const char *name, const struct file_operations *fops, void *priv, int flags); -- cgit v1.2.3-71-gd317 From 36bc08cc01709b4a9bb563b35aa530241ddc63e3 Mon Sep 17 00:00:00 2001 From: Gu Zheng Date: Tue, 16 Jul 2013 17:56:16 +0800 Subject: fs/aio: Add support to aio ring pages migration As the aio job will pin the ring pages, that will lead to mem migrated failed. In order to fix this problem we use an anon inode to manage the aio ring pages, and setup the migratepage callback in the anon inode's address space, so that when mem migrating the aio ring pages will be moved to other mem node safely. Signed-off-by: Gu Zheng Signed-off-by: Benjamin LaHaise --- fs/aio.c | 119 +++++++++++++++++++++++++++++++++++++++++++----- include/linux/migrate.h | 3 ++ mm/migrate.c | 2 +- 3 files changed, 112 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/fs/aio.c b/fs/aio.c index 9b5ca1137419..cbd0afe77273 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -35,6 +35,9 @@ #include #include #include +#include +#include +#include #include #include @@ -110,6 +113,7 @@ struct kioctx { } ____cacheline_aligned_in_smp; struct page *internal_pages[AIO_RING_PAGES]; + struct file *aio_ring_file; }; /*------ sysctl variables----*/ @@ -138,15 +142,78 @@ __initcall(aio_setup); static void aio_free_ring(struct kioctx *ctx) { - long i; + int i; + struct file *aio_ring_file = ctx->aio_ring_file; - for (i = 0; i < ctx->nr_pages; i++) + for (i = 0; i < ctx->nr_pages; i++) { + pr_debug("pid(%d) [%d] page->count=%d\n", current->pid, i, + page_count(ctx->ring_pages[i])); put_page(ctx->ring_pages[i]); + } if (ctx->ring_pages && ctx->ring_pages != ctx->internal_pages) kfree(ctx->ring_pages); + + if (aio_ring_file) { + truncate_setsize(aio_ring_file->f_inode, 0); + pr_debug("pid(%d) i_nlink=%u d_count=%d d_unhashed=%d i_count=%d\n", + current->pid, aio_ring_file->f_inode->i_nlink, + aio_ring_file->f_path.dentry->d_count, + d_unhashed(aio_ring_file->f_path.dentry), + atomic_read(&aio_ring_file->f_inode->i_count)); + fput(aio_ring_file); + ctx->aio_ring_file = NULL; + } } +static int aio_ring_mmap(struct file *file, struct vm_area_struct *vma) +{ + vma->vm_ops = &generic_file_vm_ops; + return 0; +} + +static const struct file_operations aio_ring_fops = { + .mmap = aio_ring_mmap, +}; + +static int aio_set_page_dirty(struct page *page) +{ + return 0; +} + +static int aio_migratepage(struct address_space *mapping, struct page *new, + struct page *old, enum migrate_mode mode) +{ + struct kioctx *ctx = mapping->private_data; + unsigned long flags; + unsigned idx = old->index; + int rc; + + /* Writeback must be complete */ + BUG_ON(PageWriteback(old)); + put_page(old); + + rc = migrate_page_move_mapping(mapping, new, old, NULL, mode); + if (rc != MIGRATEPAGE_SUCCESS) { + get_page(old); + return rc; + } + + get_page(new); + + spin_lock_irqsave(&ctx->completion_lock, flags); + migrate_page_copy(new, old); + ctx->ring_pages[idx] = new; + spin_unlock_irqrestore(&ctx->completion_lock, flags); + + return rc; +} + +static const struct address_space_operations aio_ctx_aops = { + .set_page_dirty = aio_set_page_dirty, + .migratepage = aio_migratepage, +}; + static int aio_setup_ring(struct kioctx *ctx) { struct aio_ring *ring; @@ -154,20 +221,45 @@ static int aio_setup_ring(struct kioctx *ctx) struct mm_struct *mm = current->mm; unsigned long size, populate; int nr_pages; + int i; + struct file *file; /* Compensate for the ring buffer's head/tail overlap entry */ nr_events += 2; /* 1 is required, 2 for good luck */ size = sizeof(struct aio_ring); size += sizeof(struct io_event) * nr_events; - nr_pages = (size + PAGE_SIZE-1) >> PAGE_SHIFT; + nr_pages = PFN_UP(size); if (nr_pages < 0) return -EINVAL; - nr_events = (PAGE_SIZE * nr_pages - sizeof(struct aio_ring)) / sizeof(struct io_event); + file = anon_inode_getfile_private("[aio]", &aio_ring_fops, ctx, O_RDWR); + if (IS_ERR(file)) { + ctx->aio_ring_file = NULL; + return -EAGAIN; + } + + file->f_inode->i_mapping->a_ops = &aio_ctx_aops; + file->f_inode->i_mapping->private_data = ctx; + file->f_inode->i_size = PAGE_SIZE * (loff_t)nr_pages; + + for (i = 0; i < nr_pages; i++) { + struct page *page; + page = find_or_create_page(file->f_inode->i_mapping, + i, GFP_HIGHUSER | __GFP_ZERO); + if (!page) + break; + pr_debug("pid(%d) page[%d]->count=%d\n", + current->pid, i, page_count(page)); + SetPageUptodate(page); + SetPageDirty(page); + unlock_page(page); + } + ctx->aio_ring_file = file; + nr_events = (PAGE_SIZE * nr_pages - sizeof(struct aio_ring)) + / sizeof(struct io_event); - ctx->nr_events = 0; ctx->ring_pages = ctx->internal_pages; if (nr_pages > AIO_RING_PAGES) { ctx->ring_pages = kcalloc(nr_pages, sizeof(struct page *), @@ -178,28 +270,31 @@ static int aio_setup_ring(struct kioctx *ctx) ctx->mmap_size = nr_pages * PAGE_SIZE; pr_debug("attempting mmap of %lu bytes\n", ctx->mmap_size); + down_write(&mm->mmap_sem); - ctx->mmap_base = do_mmap_pgoff(NULL, 0, ctx->mmap_size, - PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE, 0, &populate); + ctx->mmap_base = do_mmap_pgoff(ctx->aio_ring_file, 0, ctx->mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_POPULATE, 0, &populate); if (IS_ERR((void *)ctx->mmap_base)) { up_write(&mm->mmap_sem); ctx->mmap_size = 0; aio_free_ring(ctx); return -EAGAIN; } + up_write(&mm->mmap_sem); + + mm_populate(ctx->mmap_base, populate); pr_debug("mmap address: 0x%08lx\n", ctx->mmap_base); ctx->nr_pages = get_user_pages(current, mm, ctx->mmap_base, nr_pages, 1, 0, ctx->ring_pages, NULL); - up_write(&mm->mmap_sem); + for (i = 0; i < ctx->nr_pages; i++) + put_page(ctx->ring_pages[i]); if (unlikely(ctx->nr_pages != nr_pages)) { aio_free_ring(ctx); return -EAGAIN; } - if (populate) - mm_populate(ctx->mmap_base, populate); ctx->user_id = ctx->mmap_base; ctx->nr_events = nr_events; /* trusted copy */ @@ -399,6 +494,8 @@ out_cleanup: err = -EAGAIN; aio_free_ring(ctx); out_freectx: + if (ctx->aio_ring_file) + fput(ctx->aio_ring_file); kmem_cache_free(kioctx_cachep, ctx); pr_debug("error allocating ioctx %d\n", err); return ERR_PTR(err); diff --git a/include/linux/migrate.h b/include/linux/migrate.h index a405d3dc0f61..c407d88f5979 100644 --- a/include/linux/migrate.h +++ b/include/linux/migrate.h @@ -55,6 +55,9 @@ extern int migrate_vmas(struct mm_struct *mm, extern void migrate_page_copy(struct page *newpage, struct page *page); extern int migrate_huge_page_move_mapping(struct address_space *mapping, struct page *newpage, struct page *page); +extern int migrate_page_move_mapping(struct address_space *mapping, + struct page *newpage, struct page *page, + struct buffer_head *head, enum migrate_mode mode); #else static inline void putback_lru_pages(struct list_head *l) {} diff --git a/mm/migrate.c b/mm/migrate.c index 6f0c24438bba..1da0092561a4 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -307,7 +307,7 @@ static inline bool buffer_migrate_lock_buffers(struct buffer_head *head, * 2 for pages with a mapping * 3 for pages with a mapping and PagePrivate/PagePrivate2 set. */ -static int migrate_page_move_mapping(struct address_space *mapping, +int migrate_page_move_mapping(struct address_space *mapping, struct page *newpage, struct page *page, struct buffer_head *head, enum migrate_mode mode) { -- cgit v1.2.3-71-gd317 From 2025172e32808a327b00f968c72baa79adc594c2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 5 Jul 2013 20:07:27 +0100 Subject: spi/bitbang: Use core message pump Convert drivers using bitbang to use the core mesasge pump infrastructure, saving some code and meaning that these drivers get to take advantage of work done on improving the core implementation. Signed-off-by: Mark Brown --- drivers/spi/spi-bitbang.c | 100 ++++++++++++---------------------------- include/linux/spi/spi_bitbang.h | 4 -- 2 files changed, 30 insertions(+), 74 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 8b8487c9694e..c100875cfd42 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -255,6 +255,21 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t) * Drivers can provide word-at-a-time i/o primitives, or provide * transfer-at-a-time ones to leverage dma or fifo hardware. */ + +static int spi_bitbang_prepare_hardware(struct spi_master *spi) +{ + struct spi_bitbang *bitbang; + unsigned long flags; + + bitbang = spi_master_get_devdata(spi); + + spin_lock_irqsave(&bitbang->lock, flags); + bitbang->busy = 1; + spin_unlock_irqrestore(&bitbang->lock, flags); + + return 0; +} + static int spi_bitbang_transfer_one(struct spi_device *spi, struct spi_message *m) { @@ -346,7 +361,6 @@ static int spi_bitbang_transfer_one(struct spi_device *spi, } m->status = status; - m->complete(m->context); /* normally deactivate chipselect ... unless no error and * cs_change has hinted that the next message will probably @@ -358,54 +372,23 @@ static int spi_bitbang_transfer_one(struct spi_device *spi, ndelay(nsecs); } - return status; -} - -static void bitbang_work(struct work_struct *work) -{ - struct spi_bitbang *bitbang = - container_of(work, struct spi_bitbang, work); - unsigned long flags; - struct spi_message *m, *_m; - - spin_lock_irqsave(&bitbang->lock, flags); - bitbang->busy = 1; - list_for_each_entry_safe(m, _m, &bitbang->queue, queue) { - list_del(&m->queue); - spin_unlock_irqrestore(&bitbang->lock, flags); - - spi_bitbang_transfer_one(m->spi, m); + spi_finalize_current_message(spi->master); - spin_lock_irqsave(&bitbang->lock, flags); - } - bitbang->busy = 0; - spin_unlock_irqrestore(&bitbang->lock, flags); + return status; } -/** - * spi_bitbang_transfer - default submit to transfer queue - */ -static int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m) +static int spi_bitbang_unprepare_hardware(struct spi_master *spi) { - struct spi_bitbang *bitbang; + struct spi_bitbang *bitbang; unsigned long flags; - int status = 0; - - m->actual_length = 0; - m->status = -EINPROGRESS; - bitbang = spi_master_get_devdata(spi->master); + bitbang = spi_master_get_devdata(spi); spin_lock_irqsave(&bitbang->lock, flags); - if (!spi->max_speed_hz) - status = -ENETDOWN; - else { - list_add_tail(&m->queue, &bitbang->queue); - queue_work(bitbang->workqueue, &bitbang->work); - } + bitbang->busy = 0; spin_unlock_irqrestore(&bitbang->lock, flags); - return status; + return 0; } /*----------------------------------------------------------------------*/ @@ -436,20 +419,22 @@ static int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m) int spi_bitbang_start(struct spi_bitbang *bitbang) { struct spi_master *master = bitbang->master; - int status; if (!master || !bitbang->chipselect) return -EINVAL; - INIT_WORK(&bitbang->work, bitbang_work); spin_lock_init(&bitbang->lock); - INIT_LIST_HEAD(&bitbang->queue); if (!master->mode_bits) master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags; - if (!master->transfer) - master->transfer = spi_bitbang_transfer; + if (master->transfer || master->transfer_one_message) + return -EINVAL; + + master->prepare_transfer_hardware = spi_bitbang_prepare_hardware; + master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware; + master->transfer_one_message = spi_bitbang_transfer_one; + if (!bitbang->txrx_bufs) { bitbang->use_dma = 0; bitbang->txrx_bufs = spi_bitbang_bufs; @@ -462,32 +447,11 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) } } else if (!master->setup) return -EINVAL; - if (master->transfer == spi_bitbang_transfer && - !bitbang->setup_transfer) - return -EINVAL; - - /* this task is the only thing to touch the SPI bits */ - bitbang->busy = 0; - bitbang->workqueue = create_singlethread_workqueue( - dev_name(master->dev.parent)); - if (bitbang->workqueue == NULL) { - status = -EBUSY; - goto err1; - } /* driver may get busy before register() returns, especially * if someone registered boardinfo for devices */ - status = spi_register_master(master); - if (status < 0) - goto err2; - - return status; - -err2: - destroy_workqueue(bitbang->workqueue); -err1: - return status; + return spi_register_master(master); } EXPORT_SYMBOL_GPL(spi_bitbang_start); @@ -498,10 +462,6 @@ int spi_bitbang_stop(struct spi_bitbang *bitbang) { spi_unregister_master(bitbang->master); - WARN_ON(!list_empty(&bitbang->queue)); - - destroy_workqueue(bitbang->workqueue); - return 0; } EXPORT_SYMBOL_GPL(spi_bitbang_stop); diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h index b5aa215493f6..daebaba886aa 100644 --- a/include/linux/spi/spi_bitbang.h +++ b/include/linux/spi/spi_bitbang.h @@ -4,11 +4,7 @@ #include struct spi_bitbang { - struct workqueue_struct *workqueue; - struct work_struct work; - spinlock_t lock; - struct list_head queue; u8 busy; u8 use_dma; u8 flags; /* extra spi->mode support */ -- cgit v1.2.3-71-gd317 From 52604b1ffabac61eb07cce711f18e18ac74fbeae Mon Sep 17 00:00:00 2001 From: Shobhit Kumar Date: Thu, 11 Jul 2013 18:44:55 -0300 Subject: drm: Added SDP and VSC structures for handling PSR for eDP SDP header and SDP VSC header as per eDP 1.3 spec, section 3.5, chapter "PSR Secondary Data Package Support". v2: Modified and corrected the structures to be more in line for kernel coding guidelines and rebased the code on Paulo's DP patchset v3: removing unecessary identation at DP_RECEIVER_CAP_SIZE v4: moving them to include/drm/drm_dp_helper.h and also already icluding EDP_PSR_RECEIVER_CAP_SIZE to add everything needed for PSR at once at drm_dp_helper.h v5: Fix SDP VSC header and identation by (Paulo Zanoni) and remove i915 from title (Daniel Vetter) v6: Fix spec version and move comments from code to commit message since numbers might change in the future (by Paulo Zanoni). CC: Paulo Zanoni Reviewed-by: Paulo Zanoni Signed-off-by: Sateesh Kavuri Signed-off-by: Shobhit Kumar Signed-off-by: Rodrigo Vivi Acked-by: Dave Airlie Signed-off-by: Daniel Vetter --- include/drm/drm_dp_helper.h | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index e8e1417af3d9..ae8dbfb1207c 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -342,13 +342,42 @@ u8 drm_dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], int lane); -#define DP_RECEIVER_CAP_SIZE 0xf +#define DP_RECEIVER_CAP_SIZE 0xf +#define EDP_PSR_RECEIVER_CAP_SIZE 2 + void drm_dp_link_train_clock_recovery_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]); void drm_dp_link_train_channel_eq_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]); u8 drm_dp_link_rate_to_bw_code(int link_rate); int drm_dp_bw_code_to_link_rate(u8 link_bw); +struct edp_sdp_header { + u8 HB0; /* Secondary Data Packet ID */ + u8 HB1; /* Secondary Data Packet Type */ + u8 HB2; /* 7:5 reserved, 4:0 revision number */ + u8 HB3; /* 7:5 reserved, 4:0 number of valid data bytes */ +} __packed; + +#define EDP_SDP_HEADER_REVISION_MASK 0x1F +#define EDP_SDP_HEADER_VALID_PAYLOAD_BYTES 0x1F + +struct edp_vsc_psr { + struct edp_sdp_header sdp_header; + u8 DB0; /* Stereo Interface */ + u8 DB1; /* 0 - PSR State; 1 - Update RFB; 2 - CRC Valid */ + u8 DB2; /* CRC value bits 7:0 of the R or Cr component */ + u8 DB3; /* CRC value bits 15:8 of the R or Cr component */ + u8 DB4; /* CRC value bits 7:0 of the G or Y component */ + u8 DB5; /* CRC value bits 15:8 of the G or Y component */ + u8 DB6; /* CRC value bits 7:0 of the B or Cb component */ + u8 DB7; /* CRC value bits 15:8 of the B or Cb component */ + u8 DB8_31[24]; /* Reserved */ +} __packed; + +#define EDP_VSC_PSR_STATE_ACTIVE (1<<0) +#define EDP_VSC_PSR_UPDATE_RFB (1<<1) +#define EDP_VSC_PSR_CRC_VALUES_VALID (1<<2) + static inline int drm_dp_max_link_rate(u8 dpcd[DP_RECEIVER_CAP_SIZE]) { -- cgit v1.2.3-71-gd317 From 126a5af520eff9b99a0bb1ca4bb4a0b7973f7d5a Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Wed, 3 Jul 2013 16:30:53 +0200 Subject: KVM: kvm-io: support cookies Add new functions kvm_io_bus_{read,write}_cookie() that allows users of the kvm io infrastructure to use a cookie value to speed up lookup of a device on an io bus. Signed-off-by: Cornelia Huck Signed-off-by: Paolo Bonzini Signed-off-by: Gleb Natapov --- include/linux/kvm_host.h | 4 ++ virt/kvm/kvm_main.c | 108 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 96 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index a63d83ebd151..ec590aece366 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -160,8 +160,12 @@ enum kvm_bus { int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int len, const void *val); +int kvm_io_bus_write_cookie(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, + int len, const void *val, long cookie); int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int len, void *val); +int kvm_io_bus_read_cookie(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, + int len, void *val, long cookie); int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int len, struct kvm_io_device *dev); int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 1580dd4ace4e..71960fb03c2a 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2863,13 +2863,33 @@ static int kvm_io_bus_get_first_dev(struct kvm_io_bus *bus, return off; } +static int __kvm_io_bus_write(struct kvm_io_bus *bus, + struct kvm_io_range *range, const void *val) +{ + int idx; + + idx = kvm_io_bus_get_first_dev(bus, range->addr, range->len); + if (idx < 0) + return -EOPNOTSUPP; + + while (idx < bus->dev_count && + kvm_io_bus_sort_cmp(range, &bus->range[idx]) == 0) { + if (!kvm_iodevice_write(bus->range[idx].dev, range->addr, + range->len, val)) + return idx; + idx++; + } + + return -EOPNOTSUPP; +} + /* kvm_io_bus_write - called under kvm->slots_lock */ int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int len, const void *val) { - int idx; struct kvm_io_bus *bus; struct kvm_io_range range; + int r; range = (struct kvm_io_range) { .addr = addr, @@ -2877,14 +2897,52 @@ int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, }; bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu); - idx = kvm_io_bus_get_first_dev(bus, addr, len); + r = __kvm_io_bus_write(bus, &range, val); + return r < 0 ? r : 0; +} + +/* kvm_io_bus_write_cookie - called under kvm->slots_lock */ +int kvm_io_bus_write_cookie(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, + int len, const void *val, long cookie) +{ + struct kvm_io_bus *bus; + struct kvm_io_range range; + + range = (struct kvm_io_range) { + .addr = addr, + .len = len, + }; + + bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu); + + /* First try the device referenced by cookie. */ + if ((cookie >= 0) && (cookie < bus->dev_count) && + (kvm_io_bus_sort_cmp(&range, &bus->range[cookie]) == 0)) + if (!kvm_iodevice_write(bus->range[cookie].dev, addr, len, + val)) + return cookie; + + /* + * cookie contained garbage; fall back to search and return the + * correct cookie value. + */ + return __kvm_io_bus_write(bus, &range, val); +} + +static int __kvm_io_bus_read(struct kvm_io_bus *bus, struct kvm_io_range *range, + void *val) +{ + int idx; + + idx = kvm_io_bus_get_first_dev(bus, range->addr, range->len); if (idx < 0) return -EOPNOTSUPP; while (idx < bus->dev_count && - kvm_io_bus_sort_cmp(&range, &bus->range[idx]) == 0) { - if (!kvm_iodevice_write(bus->range[idx].dev, addr, len, val)) - return 0; + kvm_io_bus_sort_cmp(range, &bus->range[idx]) == 0) { + if (!kvm_iodevice_read(bus->range[idx].dev, range->addr, + range->len, val)) + return idx; idx++; } @@ -2895,9 +2953,9 @@ int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int len, void *val) { - int idx; struct kvm_io_bus *bus; struct kvm_io_range range; + int r; range = (struct kvm_io_range) { .addr = addr, @@ -2905,18 +2963,36 @@ int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, }; bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu); - idx = kvm_io_bus_get_first_dev(bus, addr, len); - if (idx < 0) - return -EOPNOTSUPP; + r = __kvm_io_bus_read(bus, &range, val); + return r < 0 ? r : 0; +} - while (idx < bus->dev_count && - kvm_io_bus_sort_cmp(&range, &bus->range[idx]) == 0) { - if (!kvm_iodevice_read(bus->range[idx].dev, addr, len, val)) - return 0; - idx++; - } +/* kvm_io_bus_read_cookie - called under kvm->slots_lock */ +int kvm_io_bus_read_cookie(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, + int len, void *val, long cookie) +{ + struct kvm_io_bus *bus; + struct kvm_io_range range; - return -EOPNOTSUPP; + range = (struct kvm_io_range) { + .addr = addr, + .len = len, + }; + + bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu); + + /* First try the device referenced by cookie. */ + if ((cookie >= 0) && (cookie < bus->dev_count) && + (kvm_io_bus_sort_cmp(&range, &bus->range[cookie]) == 0)) + if (!kvm_iodevice_read(bus->range[cookie].dev, addr, len, + val)) + return cookie; + + /* + * cookie contained garbage; fall back to search and return the + * correct cookie value. + */ + return __kvm_io_bus_read(bus, &range, val); } /* Caller must hold slots_lock. */ -- cgit v1.2.3-71-gd317 From e59dbe09f8e6fb8f6ee19dc79d1a2f14299e4cd2 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Thu, 4 Jul 2013 13:40:29 +0900 Subject: KVM: Introduce kvm_arch_memslots_updated() This is called right after the memslots is updated, i.e. when the result of update_memslots() gets installed in install_new_memslots(). Since the memslots needs to be updated twice when we delete or move a memslot, kvm_arch_commit_memory_region() does not correspond to this exactly. In the following patch, x86 will use this new API to check if the mmio generation has reached its maximum value, in which case mmio sptes need to be flushed out. Signed-off-by: Takuya Yoshikawa Acked-by: Alexander Graf Reviewed-by: Xiao Guangrong Signed-off-by: Paolo Bonzini --- arch/arm/kvm/arm.c | 4 ++++ arch/ia64/kvm/kvm-ia64.c | 4 ++++ arch/mips/kvm/kvm_mips.c | 4 ++++ arch/powerpc/kvm/powerpc.c | 4 ++++ arch/s390/kvm/kvm-s390.c | 4 ++++ arch/x86/kvm/x86.c | 4 ++++ include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 5 ++++- 8 files changed, 29 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index 741f66a2edbd..9c697db2787e 100644 --- a/arch/arm/kvm/arm.c +++ b/arch/arm/kvm/arm.c @@ -219,6 +219,10 @@ long kvm_arch_dev_ioctl(struct file *filp, return -EINVAL; } +void kvm_arch_memslots_updated(struct kvm *kvm) +{ +} + int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_userspace_memory_region *mem, diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 5b2dc0d10c8f..bdfd8789b376 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1560,6 +1560,10 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) return 0; } +void kvm_arch_memslots_updated(struct kvm *kvm) +{ +} + int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_userspace_memory_region *mem, diff --git a/arch/mips/kvm/kvm_mips.c b/arch/mips/kvm/kvm_mips.c index dd203e59e6fd..a7b044536de4 100644 --- a/arch/mips/kvm/kvm_mips.c +++ b/arch/mips/kvm/kvm_mips.c @@ -208,6 +208,10 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) return 0; } +void kvm_arch_memslots_updated(struct kvm *kvm) +{ +} + int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_userspace_memory_region *mem, diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 6316ee336e88..ae63ae4a1a5f 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -420,6 +420,10 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) return kvmppc_core_create_memslot(slot, npages); } +void kvm_arch_memslots_updated(struct kvm *kvm) +{ +} + int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_userspace_memory_region *mem, diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index ba694d2ba51e..a3d797b689a3 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -1056,6 +1056,10 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) return 0; } +void kvm_arch_memslots_updated(struct kvm *kvm) +{ +} + /* Section: memory related */ int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index d21bce505315..9dd8799e87c3 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -7019,6 +7019,10 @@ out_free: return -ENOMEM; } +void kvm_arch_memslots_updated(struct kvm *kvm) +{ +} + int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_userspace_memory_region *mem, diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index ec590aece366..c11c7686ae5f 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -503,6 +503,7 @@ int __kvm_set_memory_region(struct kvm *kvm, void kvm_arch_free_memslot(struct kvm_memory_slot *free, struct kvm_memory_slot *dont); int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages); +void kvm_arch_memslots_updated(struct kvm *kvm); int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *memslot, struct kvm_userspace_memory_region *mem, diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 71960fb03c2a..a86735d80ee0 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -731,7 +731,10 @@ static struct kvm_memslots *install_new_memslots(struct kvm *kvm, update_memslots(slots, new, kvm->memslots->generation); rcu_assign_pointer(kvm->memslots, slots); synchronize_srcu_expedited(&kvm->srcu); - return old_memslots; + + kvm_arch_memslots_updated(kvm); + + return old_memslots; } /* -- cgit v1.2.3-71-gd317 From e04c5d76b0cfb66cadd900cf147526f2271884b8 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Wed, 10 Jul 2013 22:21:57 -0300 Subject: remove sched notifier for cross-cpu migrations Linux as a guest on KVM hypervisor, the only user of the pvclock vsyscall interface, does not require notification on task migration because: 1. cpu ID number maps 1:1 to per-CPU pvclock time info. 2. per-CPU pvclock time info is updated if the underlying CPU changes. 3. that version is increased whenever underlying CPU changes. Which is sufficient to guarantee nanoseconds counter is calculated properly. Signed-off-by: Marcelo Tosatti Acked-by: Peter Zijlstra Signed-off-by: Gleb Natapov --- arch/x86/include/asm/pvclock.h | 1 - arch/x86/kernel/pvclock.c | 44 ------------------------------------------ arch/x86/vdso/vclock_gettime.c | 16 +++++++-------- include/linux/sched.h | 8 -------- kernel/sched/core.c | 15 -------------- 5 files changed, 8 insertions(+), 76 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/pvclock.h b/arch/x86/include/asm/pvclock.h index 109a9dd5d454..be8269b00e2a 100644 --- a/arch/x86/include/asm/pvclock.h +++ b/arch/x86/include/asm/pvclock.h @@ -93,7 +93,6 @@ unsigned __pvclock_read_cycles(const struct pvclock_vcpu_time_info *src, struct pvclock_vsyscall_time_info { struct pvclock_vcpu_time_info pvti; - u32 migrate_count; } __attribute__((__aligned__(SMP_CACHE_BYTES))); #define PVTI_SIZE sizeof(struct pvclock_vsyscall_time_info) diff --git a/arch/x86/kernel/pvclock.c b/arch/x86/kernel/pvclock.c index 2cb9470ea85b..a16bae3f83b3 100644 --- a/arch/x86/kernel/pvclock.c +++ b/arch/x86/kernel/pvclock.c @@ -128,46 +128,7 @@ void pvclock_read_wallclock(struct pvclock_wall_clock *wall_clock, set_normalized_timespec(ts, now.tv_sec, now.tv_nsec); } -static struct pvclock_vsyscall_time_info *pvclock_vdso_info; - -static struct pvclock_vsyscall_time_info * -pvclock_get_vsyscall_user_time_info(int cpu) -{ - if (!pvclock_vdso_info) { - BUG(); - return NULL; - } - - return &pvclock_vdso_info[cpu]; -} - -struct pvclock_vcpu_time_info *pvclock_get_vsyscall_time_info(int cpu) -{ - return &pvclock_get_vsyscall_user_time_info(cpu)->pvti; -} - #ifdef CONFIG_X86_64 -static int pvclock_task_migrate(struct notifier_block *nb, unsigned long l, - void *v) -{ - struct task_migration_notifier *mn = v; - struct pvclock_vsyscall_time_info *pvti; - - pvti = pvclock_get_vsyscall_user_time_info(mn->from_cpu); - - /* this is NULL when pvclock vsyscall is not initialized */ - if (unlikely(pvti == NULL)) - return NOTIFY_DONE; - - pvti->migrate_count++; - - return NOTIFY_DONE; -} - -static struct notifier_block pvclock_migrate = { - .notifier_call = pvclock_task_migrate, -}; - /* * Initialize the generic pvclock vsyscall state. This will allocate * a/some page(s) for the per-vcpu pvclock information, set up a @@ -181,17 +142,12 @@ int __init pvclock_init_vsyscall(struct pvclock_vsyscall_time_info *i, WARN_ON (size != PVCLOCK_VSYSCALL_NR_PAGES*PAGE_SIZE); - pvclock_vdso_info = i; - for (idx = 0; idx <= (PVCLOCK_FIXMAP_END-PVCLOCK_FIXMAP_BEGIN); idx++) { __set_fixmap(PVCLOCK_FIXMAP_BEGIN + idx, __pa(i) + (idx*PAGE_SIZE), PAGE_KERNEL_VVAR); } - - register_task_migration_notifier(&pvclock_migrate); - return 0; } #endif diff --git a/arch/x86/vdso/vclock_gettime.c b/arch/x86/vdso/vclock_gettime.c index c74436e687bf..72074d528400 100644 --- a/arch/x86/vdso/vclock_gettime.c +++ b/arch/x86/vdso/vclock_gettime.c @@ -85,15 +85,18 @@ static notrace cycle_t vread_pvclock(int *mode) cycle_t ret; u64 last; u32 version; - u32 migrate_count; u8 flags; unsigned cpu, cpu1; /* - * When looping to get a consistent (time-info, tsc) pair, we - * also need to deal with the possibility we can switch vcpus, - * so make sure we always re-fetch time-info for the current vcpu. + * Note: hypervisor must guarantee that: + * 1. cpu ID number maps 1:1 to per-CPU pvclock time info. + * 2. that per-CPU pvclock time info is updated if the + * underlying CPU changes. + * 3. that version is increased whenever underlying CPU + * changes. + * */ do { cpu = __getcpu() & VGETCPU_CPU_MASK; @@ -104,8 +107,6 @@ static notrace cycle_t vread_pvclock(int *mode) pvti = get_pvti(cpu); - migrate_count = pvti->migrate_count; - version = __pvclock_read_cycles(&pvti->pvti, &ret, &flags); /* @@ -117,8 +118,7 @@ static notrace cycle_t vread_pvclock(int *mode) cpu1 = __getcpu() & VGETCPU_CPU_MASK; } while (unlikely(cpu != cpu1 || (pvti->pvti.version & 1) || - pvti->pvti.version != version || - pvti->migrate_count != migrate_count)); + pvti->pvti.version != version)); if (unlikely(!(flags & PVCLOCK_TSC_STABLE_BIT))) *mode = VCLOCK_NONE; diff --git a/include/linux/sched.h b/include/linux/sched.h index 50d04b92ceda..bfc809d51745 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -107,14 +107,6 @@ extern unsigned long this_cpu_load(void); extern void calc_global_load(unsigned long ticks); extern void update_cpu_load_nohz(void); -/* Notifier for when a task gets migrated to a new CPU */ -struct task_migration_notifier { - struct task_struct *task; - int from_cpu; - int to_cpu; -}; -extern void register_task_migration_notifier(struct notifier_block *n); - extern unsigned long get_parent_ip(unsigned long addr); extern void dump_cpu_task(int cpu); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 0d8eb4525e76..0efd2eefb027 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -976,13 +976,6 @@ void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags) rq->skip_clock_update = 1; } -static ATOMIC_NOTIFIER_HEAD(task_migration_notifier); - -void register_task_migration_notifier(struct notifier_block *n) -{ - atomic_notifier_chain_register(&task_migration_notifier, n); -} - #ifdef CONFIG_SMP void set_task_cpu(struct task_struct *p, unsigned int new_cpu) { @@ -1013,18 +1006,10 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu) trace_sched_migrate_task(p, new_cpu); if (task_cpu(p) != new_cpu) { - struct task_migration_notifier tmn; - if (p->sched_class->migrate_task_rq) p->sched_class->migrate_task_rq(p, new_cpu); p->se.nr_migrations++; perf_sw_event(PERF_COUNT_SW_CPU_MIGRATIONS, 1, NULL, 0); - - tmn.task = p; - tmn.from_cpu = task_cpu(p); - tmn.to_cpu = new_cpu; - - atomic_notifier_call_chain(&task_migration_notifier, 0, &tmn); } __set_task_cpu(p, new_cpu); -- cgit v1.2.3-71-gd317 From 078726ce6d56a767533064e0f2f2100d7cb6fc22 Mon Sep 17 00:00:00 2001 From: Sourav Poddar Date: Thu, 18 Jul 2013 15:31:25 +0530 Subject: driver: spi: Modify core to compute the message length Make spi core calculate the message length while populating the other transfer parameters. Usecase, driver can use it to populate framelength filed in their controller. Signed-off-by: Sourav Poddar Signed-off-by: Mark Brown --- drivers/spi/spi.c | 1 + include/linux/spi/spi.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 978dda2c5239..7e3446cab72f 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1375,6 +1375,7 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) * it is not set for this transfer. */ list_for_each_entry(xfer, &message->transfers, transfer_list) { + message->frame_length += xfer->len; if (!xfer->bits_per_word) xfer->bits_per_word = spi->bits_per_word; if (!xfer->speed_hz) diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 28e440be1c07..aadd0a885a0f 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -578,6 +578,7 @@ struct spi_message { /* completion is reported through a callback */ void (*complete)(void *context); void *context; + unsigned frame_length; unsigned actual_length; int status; -- cgit v1.2.3-71-gd317 From cce723ed091ac304d48386bcc3524994c345123e Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Fri, 19 Jul 2013 09:16:42 -0700 Subject: drm/i915: Make i915 events part of uapi Make the uevent strings part of the user API for people who wish to write their own listeners. v2: Make a space in the string concatenation. (Chad) Use the "UEVENT" suffix intead of "EVENT" (Chad) Make kernel-doc parseable Docbook comments (Daniel) v3: Undid reset change introduced in last submission (Daniel) Fixed up comments to address removal changes. Thanks to Daniel Vetter for a majority of the parity error comments. CC: Chad Versace Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 8 ++++---- include/uapi/drm/i915_drm.h | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 1fffb4b10a05..58ee8269471f 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -812,7 +812,7 @@ static void ivybridge_parity_work(struct work_struct *work) mutex_unlock(&dev_priv->dev->struct_mutex); - parity_event[0] = "L3_PARITY_ERROR=1"; + parity_event[0] = I915_L3_PARITY_UEVENT "=1"; parity_event[1] = kasprintf(GFP_KERNEL, "ROW=%d", row); parity_event[2] = kasprintf(GFP_KERNEL, "BANK=%d", bank); parity_event[3] = kasprintf(GFP_KERNEL, "SUBBANK=%d", subbank); @@ -1402,9 +1402,9 @@ static void i915_error_work_func(struct work_struct *work) gpu_error); struct drm_device *dev = dev_priv->dev; struct intel_ring_buffer *ring; - char *error_event[] = { "ERROR=1", NULL }; - char *reset_event[] = { "RESET=1", NULL }; - char *reset_done_event[] = { "ERROR=0", NULL }; + char *error_event[] = { I915_ERROR_UEVENT "=1", NULL }; + char *reset_event[] = { I915_RESET_UEVENT "=1", NULL }; + char *reset_done_event[] = { I915_ERROR_UEVENT "=0", NULL }; int i, ret; kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 923ed7fe5775..a1a7b6bd60d8 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -33,6 +33,30 @@ * subject to backwards-compatibility constraints. */ +/** + * DOC: uevents generated by i915 on it's device node + * + * I915_L3_PARITY_UEVENT - Generated when the driver receives a parity mismatch + * event from the gpu l3 cache. Additional information supplied is ROW, + * BANK, SUBBANK of the affected cacheline. Userspace should keep track of + * these events and if a specific cache-line seems to have a persistent + * error remap it with the l3 remapping tool supplied in intel-gpu-tools. + * The value supplied with the event is always 1. + * + * I915_ERROR_UEVENT - Generated upon error detection, currently only via + * hangcheck. The error detection event is a good indicator of when things + * began to go badly. The value supplied with the event is a 1 upon error + * detection, and a 0 upon reset completion, signifying no more error + * exists. NOTE: Disabling hangcheck or reset via module parameter will + * cause the related events to not be seen. + * + * I915_RESET_UEVENT - Event is generated just before an attempt to reset the + * the GPU. The value supplied with the event is always 1. NOTE: Disable + * reset via module parameter will cause this event to not be seen. + */ +#define I915_L3_PARITY_UEVENT "L3_PARITY_ERROR" +#define I915_ERROR_UEVENT "ERROR" +#define I915_RESET_UEVENT "RESET" /* Each region is a minimum of 16k, and there are at most 255 of them. */ -- cgit v1.2.3-71-gd317 From 640efa08cb635ae43d5ceae302b20c2c3f2035e5 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 3 Jul 2013 13:14:32 +0900 Subject: gpio: em: Add pinctrl support Register the GPIO pin range, and request and free GPIO pins using the pinctrl API. The pctl_name platform data member should be used by platform devices to point out which pinctrl device to use. Follows same style as "dc3465a gpio-rcar: Add pinctrl support", by Laurent Pinchart, thanks to him. Signed-off-by: Magnus Damm Signed-off-by: Linus Walleij --- drivers/gpio/gpio-em.c | 25 +++++++++++++++++++++++++ include/linux/platform_data/gpio-em.h | 1 + 2 files changed, 26 insertions(+) (limited to 'include') diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index 5cba855638bf..eca119b58c21 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -30,6 +30,7 @@ #include #include #include +#include #include struct em_gio_priv { @@ -216,6 +217,21 @@ static int em_gio_to_irq(struct gpio_chip *chip, unsigned offset) return irq_create_mapping(gpio_to_priv(chip)->irq_domain, offset); } +static int em_gio_request(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_request_gpio(chip->base + offset); +} + +static void em_gio_free(struct gpio_chip *chip, unsigned offset) +{ + pinctrl_free_gpio(chip->base + offset); + + /* Set the GPIO as an input to ensure that the next GPIO request won't + * drive the GPIO pin as an output. + */ + em_gio_direction_input(chip, offset); +} + static int em_gio_irq_domain_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { @@ -308,6 +324,8 @@ static int em_gio_probe(struct platform_device *pdev) gpio_chip->direction_output = em_gio_direction_output; gpio_chip->set = em_gio_set; gpio_chip->to_irq = em_gio_to_irq; + gpio_chip->request = em_gio_request; + gpio_chip->free = em_gio_free; gpio_chip->label = name; gpio_chip->owner = THIS_MODULE; gpio_chip->base = pdata->gpio_base; @@ -351,6 +369,13 @@ static int em_gio_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to add GPIO controller\n"); goto err1; } + + if (pdata->pctl_name) { + ret = gpiochip_add_pin_range(gpio_chip, pdata->pctl_name, 0, + gpio_chip->base, gpio_chip->ngpio); + if (ret < 0) + dev_warn(&pdev->dev, "failed to add pin range\n"); + } return 0; err1: diff --git a/include/linux/platform_data/gpio-em.h b/include/linux/platform_data/gpio-em.h index 573edfb046c4..7c5a519d2dcd 100644 --- a/include/linux/platform_data/gpio-em.h +++ b/include/linux/platform_data/gpio-em.h @@ -5,6 +5,7 @@ struct gpio_em_config { unsigned int gpio_base; unsigned int irq_base; unsigned int number_of_pins; + const char *pctl_name; }; #endif /* __GPIO_EM_H__ */ -- cgit v1.2.3-71-gd317 From ae3e4c277669e16093f0e71ca927cf33e7dde053 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 16 Jul 2013 12:32:08 +0200 Subject: leds: Remove leds-renesas-tpu driver The driver is superseded by the generic pwm-renesas-tpu driver used with leds-pwm. Signed-off-by: Laurent Pinchart Acked-by: Bryan Wu Signed-off-by: Simon Horman --- drivers/leds/Kconfig | 12 - drivers/leds/Makefile | 1 - drivers/leds/leds-renesas-tpu.c | 337 ------------------------- include/linux/platform_data/leds-renesas-tpu.h | 14 - 4 files changed, 364 deletions(-) delete mode 100644 drivers/leds/leds-renesas-tpu.c delete mode 100644 include/linux/platform_data/leds-renesas-tpu.h (limited to 'include') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index e43402dd1dea..074bcb3892b5 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -429,18 +429,6 @@ config LEDS_ASIC3 cannot be used. This driver supports hardware blinking with an on+off period from 62ms to 125s. Say Y to enable LEDs on the HP iPAQ hx4700. -config LEDS_RENESAS_TPU - bool "LED support for Renesas TPU" - depends on LEDS_CLASS=y && HAVE_CLK && GPIOLIB - help - This option enables build of the LED TPU platform driver, - suitable to drive any TPU channel on newer Renesas SoCs. - The driver controls the GPIO pin connected to the LED via - the GPIO framework and expects the LED to be connected to - a pin that can be driven in both GPIO mode and using TPU - pin function. The latter to support brightness control. - Brightness control is supported but hardware blinking is not. - config LEDS_TCA6507 tristate "LED Support for TCA6507 I2C chip" depends on LEDS_CLASS && I2C diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index ac2897732b02..ae4b6135f665 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -49,7 +49,6 @@ obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o obj-$(CONFIG_LEDS_NS2) += leds-ns2.o obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o -obj-$(CONFIG_LEDS_RENESAS_TPU) += leds-renesas-tpu.o obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o diff --git a/drivers/leds/leds-renesas-tpu.c b/drivers/leds/leds-renesas-tpu.c deleted file mode 100644 index adebf4931e1e..000000000000 --- a/drivers/leds/leds-renesas-tpu.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * LED control using Renesas TPU - * - * Copyright (C) 2011 Magnus Damm - * - * 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; either 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -enum r_tpu_pin { R_TPU_PIN_UNUSED, R_TPU_PIN_GPIO, R_TPU_PIN_GPIO_FN }; -enum r_tpu_timer { R_TPU_TIMER_UNUSED, R_TPU_TIMER_ON }; - -struct r_tpu_priv { - struct led_classdev ldev; - void __iomem *mapbase; - struct clk *clk; - struct platform_device *pdev; - enum r_tpu_pin pin_state; - enum r_tpu_timer timer_state; - unsigned long min_rate; - unsigned int refresh_rate; - struct work_struct work; - enum led_brightness new_brightness; -}; - -static DEFINE_SPINLOCK(r_tpu_lock); - -#define TSTR -1 /* Timer start register (shared register) */ -#define TCR 0 /* Timer control register (+0x00) */ -#define TMDR 1 /* Timer mode register (+0x04) */ -#define TIOR 2 /* Timer I/O control register (+0x08) */ -#define TIER 3 /* Timer interrupt enable register (+0x0c) */ -#define TSR 4 /* Timer status register (+0x10) */ -#define TCNT 5 /* Timer counter (+0x14) */ -#define TGRA 6 /* Timer general register A (+0x18) */ -#define TGRB 7 /* Timer general register B (+0x1c) */ -#define TGRC 8 /* Timer general register C (+0x20) */ -#define TGRD 9 /* Timer general register D (+0x24) */ - -static inline u16 r_tpu_read(struct r_tpu_priv *p, int reg_nr) -{ - struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; - void __iomem *base = p->mapbase; - unsigned long offs = reg_nr << 2; - - if (reg_nr == TSTR) - return ioread16(base - cfg->channel_offset); - - return ioread16(base + offs); -} - -static inline void r_tpu_write(struct r_tpu_priv *p, int reg_nr, u16 value) -{ - struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; - void __iomem *base = p->mapbase; - unsigned long offs = reg_nr << 2; - - if (reg_nr == TSTR) { - iowrite16(value, base - cfg->channel_offset); - return; - } - - iowrite16(value, base + offs); -} - -static void r_tpu_start_stop_ch(struct r_tpu_priv *p, int start) -{ - struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; - unsigned long flags; - u16 value; - - /* start stop register shared by multiple timer channels */ - spin_lock_irqsave(&r_tpu_lock, flags); - value = r_tpu_read(p, TSTR); - - if (start) - value |= 1 << cfg->timer_bit; - else - value &= ~(1 << cfg->timer_bit); - - r_tpu_write(p, TSTR, value); - spin_unlock_irqrestore(&r_tpu_lock, flags); -} - -static int r_tpu_enable(struct r_tpu_priv *p, enum led_brightness brightness) -{ - struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; - int prescaler[] = { 1, 4, 16, 64 }; - int k, ret; - unsigned long rate, tmp; - - if (p->timer_state == R_TPU_TIMER_ON) - return 0; - - /* wake up device and enable clock */ - pm_runtime_get_sync(&p->pdev->dev); - ret = clk_enable(p->clk); - if (ret) { - dev_err(&p->pdev->dev, "cannot enable clock\n"); - return ret; - } - - /* make sure channel is disabled */ - r_tpu_start_stop_ch(p, 0); - - /* get clock rate after enabling it */ - rate = clk_get_rate(p->clk); - - /* pick the lowest acceptable rate */ - for (k = ARRAY_SIZE(prescaler) - 1; k >= 0; k--) - if ((rate / prescaler[k]) >= p->min_rate) - break; - - if (k < 0) { - dev_err(&p->pdev->dev, "clock rate mismatch\n"); - goto err0; - } - dev_dbg(&p->pdev->dev, "rate = %lu, prescaler %u\n", - rate, prescaler[k]); - - /* clear TCNT on TGRB match, count on rising edge, set prescaler */ - r_tpu_write(p, TCR, 0x0040 | k); - - /* output 0 until TGRA, output 1 until TGRB */ - r_tpu_write(p, TIOR, 0x0002); - - rate /= prescaler[k] * p->refresh_rate; - r_tpu_write(p, TGRB, rate); - dev_dbg(&p->pdev->dev, "TRGB = 0x%04lx\n", rate); - - tmp = (cfg->max_brightness - brightness) * rate; - r_tpu_write(p, TGRA, tmp / cfg->max_brightness); - dev_dbg(&p->pdev->dev, "TRGA = 0x%04lx\n", tmp / cfg->max_brightness); - - /* PWM mode */ - r_tpu_write(p, TMDR, 0x0002); - - /* enable channel */ - r_tpu_start_stop_ch(p, 1); - - p->timer_state = R_TPU_TIMER_ON; - return 0; - err0: - clk_disable(p->clk); - pm_runtime_put_sync(&p->pdev->dev); - return -ENOTSUPP; -} - -static void r_tpu_disable(struct r_tpu_priv *p) -{ - if (p->timer_state == R_TPU_TIMER_UNUSED) - return; - - /* disable channel */ - r_tpu_start_stop_ch(p, 0); - - /* stop clock and mark device as idle */ - clk_disable(p->clk); - pm_runtime_put_sync(&p->pdev->dev); - - p->timer_state = R_TPU_TIMER_UNUSED; -} - -static void r_tpu_set_pin(struct r_tpu_priv *p, enum r_tpu_pin new_state, - enum led_brightness brightness) -{ - struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; - - if (p->pin_state == new_state) { - if (p->pin_state == R_TPU_PIN_GPIO) - gpio_set_value(cfg->pin_gpio, brightness); - return; - } - - if (p->pin_state == R_TPU_PIN_GPIO) - gpio_free(cfg->pin_gpio); - - if (p->pin_state == R_TPU_PIN_GPIO_FN) - gpio_free(cfg->pin_gpio_fn); - - if (new_state == R_TPU_PIN_GPIO) - gpio_request_one(cfg->pin_gpio, !!brightness ? - GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, - cfg->name); - - if (new_state == R_TPU_PIN_GPIO_FN) - gpio_request(cfg->pin_gpio_fn, cfg->name); - - p->pin_state = new_state; -} - -static void r_tpu_work(struct work_struct *work) -{ - struct r_tpu_priv *p = container_of(work, struct r_tpu_priv, work); - enum led_brightness brightness = p->new_brightness; - - r_tpu_disable(p); - - /* off and maximum are handled as GPIO pins, in between PWM */ - if ((brightness == 0) || (brightness == p->ldev.max_brightness)) - r_tpu_set_pin(p, R_TPU_PIN_GPIO, brightness); - else { - r_tpu_set_pin(p, R_TPU_PIN_GPIO_FN, 0); - r_tpu_enable(p, brightness); - } -} - -static void r_tpu_set_brightness(struct led_classdev *ldev, - enum led_brightness brightness) -{ - struct r_tpu_priv *p = container_of(ldev, struct r_tpu_priv, ldev); - p->new_brightness = brightness; - schedule_work(&p->work); -} - -static int r_tpu_probe(struct platform_device *pdev) -{ - struct led_renesas_tpu_config *cfg = pdev->dev.platform_data; - struct r_tpu_priv *p; - struct resource *res; - int ret; - - if (!cfg) { - dev_err(&pdev->dev, "missing platform data\n"); - return -ENODEV; - } - - p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); - if (p == NULL) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); - return -ENOMEM; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "failed to get I/O memory\n"); - return -ENXIO; - } - - /* map memory, let mapbase point to our channel */ - p->mapbase = devm_ioremap_nocache(&pdev->dev, res->start, - resource_size(res)); - if (p->mapbase == NULL) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); - return -ENXIO; - } - - /* get hold of clock */ - p->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(p->clk)) { - dev_err(&pdev->dev, "cannot get clock\n"); - return PTR_ERR(p->clk); - } - - p->pdev = pdev; - p->pin_state = R_TPU_PIN_UNUSED; - p->timer_state = R_TPU_TIMER_UNUSED; - p->refresh_rate = cfg->refresh_rate ? cfg->refresh_rate : 100; - r_tpu_set_pin(p, R_TPU_PIN_GPIO, LED_OFF); - platform_set_drvdata(pdev, p); - - INIT_WORK(&p->work, r_tpu_work); - - p->ldev.name = cfg->name; - p->ldev.brightness = LED_OFF; - p->ldev.max_brightness = cfg->max_brightness; - p->ldev.brightness_set = r_tpu_set_brightness; - p->ldev.flags |= LED_CORE_SUSPENDRESUME; - ret = led_classdev_register(&pdev->dev, &p->ldev); - if (ret < 0) - goto err0; - - /* max_brightness may be updated by the LED core code */ - p->min_rate = p->ldev.max_brightness * p->refresh_rate; - - pm_runtime_enable(&pdev->dev); - return 0; - - err0: - r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF); - return ret; -} - -static int r_tpu_remove(struct platform_device *pdev) -{ - struct r_tpu_priv *p = platform_get_drvdata(pdev); - - r_tpu_set_brightness(&p->ldev, LED_OFF); - led_classdev_unregister(&p->ldev); - cancel_work_sync(&p->work); - r_tpu_disable(p); - r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF); - - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static struct platform_driver r_tpu_device_driver = { - .probe = r_tpu_probe, - .remove = r_tpu_remove, - .driver = { - .name = "leds-renesas-tpu", - } -}; - -module_platform_driver(r_tpu_device_driver); - -MODULE_AUTHOR("Magnus Damm"); -MODULE_DESCRIPTION("Renesas TPU LED Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/leds-renesas-tpu.h b/include/linux/platform_data/leds-renesas-tpu.h deleted file mode 100644 index 055387086fc1..000000000000 --- a/include/linux/platform_data/leds-renesas-tpu.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __LEDS_RENESAS_TPU_H__ -#define __LEDS_RENESAS_TPU_H__ - -struct led_renesas_tpu_config { - char *name; - unsigned pin_gpio_fn; - unsigned pin_gpio; - unsigned int channel_offset; - unsigned int timer_bit; - unsigned int max_brightness; - unsigned int refresh_rate; -}; - -#endif /* __LEDS_RENESAS_TPU_H__ */ -- cgit v1.2.3-71-gd317 From 75b9222556ede41b9bd9d2d0dcb998f668b49b5f Mon Sep 17 00:00:00 2001 From: Nathaniel Yazdani Date: Wed, 3 Jul 2013 13:52:21 -0700 Subject: jbd: remove unneeded semicolon This patch removes an unnecessary semicolon that was placed after the closing bracket of an inline JBD wrapper function. Signed-off-by: Nathaniel Yazdani Signed-off-by: Jan Kara --- include/linux/jbd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/jbd.h b/include/linux/jbd.h index 8685d1be12c7..bc3e57d367fc 100644 --- a/include/linux/jbd.h +++ b/include/linux/jbd.h @@ -77,7 +77,7 @@ static inline void *jbd_alloc(size_t size, gfp_t flags) static inline void jbd_free(void *ptr, size_t size) { free_pages((unsigned long)ptr, get_order(size)); -}; +} #define JFS_MIN_JOURNAL_BLOCKS 1024 -- cgit v1.2.3-71-gd317 From 27ce405039bfe6d3f4143415c638f56a3df77dca Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 10 Jul 2013 19:56:27 +0200 Subject: HID: fix data access in implement() implement() is setting bytes in LE data stream. In case the data is not aligned to 64bits, it reads past the allocated buffer. It doesn't really change any value there (it's properly bitmasked), but in case that this read past the boundary hits a page boundary, pagefault happens when accessing 64bits of 'x' in implement(), and kernel oopses. This happens much more often when numbered reports are in use, as the initial 8bit skip in the buffer makes the whole process work on values which are not aligned to 64bits. This problem dates back to attempts in 2005 and 2006 to make implement() and extract() as generic as possible, and even back then the problem was realized by Adam Kroperlin, but falsely assumed to be impossible to cause any harm: http://www.mail-archive.com/linux-usb-devel@lists.sourceforge.net/msg47690.html I have made several attempts at fixing it "on the spot" directly in implement(), but the results were horrible; the special casing for processing last 64bit chunk and switching to different math makes it unreadable mess. I therefore took a path to allocate a few bytes more which will never make it into final report, but are there as a cushion for all the 64bit math operations happening in implement() and extract(). All callers of hid_output_report() are converted at the same time to allocate the buffer by newly introduced hid_alloc_report_buf() helper. Bruno noticed that the whole raw_size test can be dropped as well, as hid_alloc_report_buf() makes sure that the buffer is always of a proper size. Reviewed-by: Benjamin Tissoires Acked-by: Gustavo Padovan Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 19 ++++++++++++++++++- drivers/hid/hid-logitech-dj.c | 12 ++++++++++-- drivers/hid/hid-picolcd_debugfs.c | 23 ++++++++++++----------- drivers/hid/usbhid/hid-core.c | 5 ++--- include/linux/hid.h | 1 + net/bluetooth/hidp/core.c | 14 +++++++++----- 6 files changed, 52 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b0f2f459f59b..a1b248cea5b0 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1128,7 +1128,8 @@ static void hid_output_field(const struct hid_device *hid, } /* - * Create a report. + * Create a report. 'data' has to be allocated using + * hid_alloc_report_buf() so that it has proper size. */ void hid_output_report(struct hid_report *report, __u8 *data) @@ -1144,6 +1145,22 @@ void hid_output_report(struct hid_report *report, __u8 *data) } EXPORT_SYMBOL_GPL(hid_output_report); +/* + * Allocator for buffer that is going to be passed to hid_output_report() + */ +u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) +{ + /* + * 7 extra bytes are necessary to achieve proper functionality + * of implement() working on 8 byte chunks + */ + + int len = ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7; + + return kmalloc(len, flags); +} +EXPORT_SYMBOL_GPL(hid_alloc_report_buf); + /* * Set a field value. The report this field belongs to has to be * created and transferred to the device, to set this value in the diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 5207591a598c..4d792739dbd1 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -574,7 +574,7 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, struct hid_field *field; struct hid_report *report; - unsigned char data[8]; + unsigned char *data; int offset; dbg_hid("%s: %s, type:%d | code:%d | value:%d\n", @@ -590,6 +590,13 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, return -1; } hid_set_field(field, offset, value); + + data = hid_alloc_report_buf(field->report, GFP_KERNEL); + if (!data) { + dev_warn(&dev->dev, "failed to allocate report buf memory\n"); + return -1; + } + hid_output_report(field->report, &data[0]); output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT]; @@ -600,8 +607,9 @@ static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, hid_hw_request(dj_rcv_hiddev, report, HID_REQ_SET_REPORT); - return 0; + kfree(data); + return 0; } static int logi_dj_ll_start(struct hid_device *hid) diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c index 59ab8e157e6b..024cdf3c2297 100644 --- a/drivers/hid/hid-picolcd_debugfs.c +++ b/drivers/hid/hid-picolcd_debugfs.c @@ -394,7 +394,7 @@ static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data, void picolcd_debug_out_report(struct picolcd_data *data, struct hid_device *hdev, struct hid_report *report) { - u8 raw_data[70]; + u8 *raw_data; int raw_size = (report->size >> 3) + 1; char *buff; #define BUFF_SZ 256 @@ -407,20 +407,20 @@ void picolcd_debug_out_report(struct picolcd_data *data, if (!buff) return; - snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ", - report->id, raw_size); - hid_debug_event(hdev, buff); - if (raw_size + 5 > sizeof(raw_data)) { + raw_data = hid_alloc_report_buf(report, GFP_ATOMIC); + if (!raw_data) { kfree(buff); - hid_debug_event(hdev, " TOO BIG\n"); return; - } else { - raw_data[0] = report->id; - hid_output_report(report, raw_data); - dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); - hid_debug_event(hdev, buff); } + snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ", + report->id, raw_size); + hid_debug_event(hdev, buff); + raw_data[0] = report->id; + hid_output_report(report, raw_data); + dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size); + hid_debug_event(hdev, buff); + switch (report->id) { case REPORT_LED_STATE: /* 1 data byte with GPO state */ @@ -644,6 +644,7 @@ void picolcd_debug_out_report(struct picolcd_data *data, break; } wake_up_interruptible(&hdev->debug_wait); + kfree(raw_data); kfree(buff); } diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 99418285222c..ada164e1b3a1 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -535,7 +535,6 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re { int head; struct usbhid_device *usbhid = hid->driver_data; - int len = ((report->size - 1) >> 3) + 1 + (report->id > 0); if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) return; @@ -546,7 +545,7 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re return; } - usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC); + usbhid->out[usbhid->outhead].raw_report = hid_alloc_report_buf(report, GFP_ATOMIC); if (!usbhid->out[usbhid->outhead].raw_report) { hid_warn(hid, "output queueing failed\n"); return; @@ -595,7 +594,7 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re } if (dir == USB_DIR_OUT) { - usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC); + usbhid->ctrl[usbhid->ctrlhead].raw_report = hid_alloc_report_buf(report, GFP_ATOMIC); if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) { hid_warn(hid, "control queueing failed\n"); return; diff --git a/include/linux/hid.h b/include/linux/hid.h index 0c48991b0402..acccdf4eb485 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -744,6 +744,7 @@ struct hid_field *hidinput_get_led_field(struct hid_device *hid); unsigned int hidinput_count_leds(struct hid_device *hid); __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code); void hid_output_report(struct hid_report *report, __u8 *data); +u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags); struct hid_device *hid_allocate_device(void); struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 46c6a148f0b3..212980ff99b9 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -231,17 +231,21 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) static int hidp_send_report(struct hidp_session *session, struct hid_report *report) { - unsigned char buf[32], hdr; - int rsize; + unsigned char hdr; + u8 *buf; + int rsize, ret; - rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0); - if (rsize > sizeof(buf)) + buf = hid_alloc_report_buf(report, GFP_ATOMIC); + if (!buf) return -EIO; hid_output_report(report, buf); hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; - return hidp_send_intr_message(session, hdr, buf, rsize); + ret = hidp_send_intr_message(session, hdr, buf, rsize); + + kfree(buf); + return ret; } static int hidp_get_raw_report(struct hid_device *hid, -- cgit v1.2.3-71-gd317 From bb4997a1afbff61084b243d62aaaf23ea38a290e Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Mon, 15 Jul 2013 13:15:04 +0200 Subject: bcma: add some more core names These cores were found on a BCM4708 (chipid 53010), this is a ARM SoC with two Cortex A9 cores. bcma: bus0: Found chip with id 0xCF12, rev 0x00 and package 0x02 bcma: bus0: Core 0 found: ChipCommon (manuf 0x4BF, id 0x800, rev 0x2A, class 0x0) bcma: bus0: Core 1 found: DMA (manuf 0x4BF, id 0x502, rev 0x01, class 0x0) bcma: bus0: Core 2 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0) bcma: bus0: Core 3 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0) bcma: bus0: Core 4 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0) bcma: bus0: Core 5 found: GBit MAC (manuf 0x4BF, id 0x82D, rev 0x04, class 0x0) bcma: bus0: Core 6 found: PCIe Gen 2 (manuf 0x4BF, id 0x501, rev 0x01, class 0x0) bcma: bus0: Core 7 found: PCIe Gen 2 (manuf 0x4BF, id 0x501, rev 0x01, class 0x0) bcma: bus0: Core 8 found: ARM Cortex A9 core (ihost) (manuf 0x4BF, id 0x510, rev 0x01, class 0x0) bcma: bus0: Core 9 found: USB 2.0 (manuf 0x4BF, id 0x504, rev 0x01, class 0x0) bcma: bus0: Core 10 found: USB 3.0 (manuf 0x4BF, id 0x505, rev 0x01, class 0x0) bcma: bus0: Core 11 found: SDIO3 (manuf 0x4BF, id 0x503, rev 0x01, class 0x0) bcma: bus0: Core 12 found: ARM Cortex A9 JTAG (manuf 0x4BF, id 0x506, rev 0x01, class 0x0) bcma: bus0: Core 13 found: Denali DDR2/DDR3 memory controller (manuf 0x4BF, id 0x507, rev 0x01, class 0x0) bcma: bus0: Core 14 found: ROM (manuf 0x4BF, id 0x508, rev 0x01, class 0x0) bcma: bus0: Core 15 found: NAND flash controller (manuf 0x4BF, id 0x509, rev 0x01, class 0x0) bcma: bus0: Core 16 found: SPI flash controller (manuf 0x4BF, id 0x50A, rev 0x01, class 0x0) Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/scan.c | 12 ++++++++++++ include/linux/bcma/bcma.h | 12 ++++++++++++ 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c index 8bffa5c9818c..f2f1415376db 100644 --- a/drivers/bcma/scan.c +++ b/drivers/bcma/scan.c @@ -32,6 +32,18 @@ static const struct bcma_device_id_name bcma_bcm_device_names[] = { { BCMA_CORE_4706_CHIPCOMMON, "BCM4706 ChipCommon" }, { BCMA_CORE_4706_SOC_RAM, "BCM4706 SOC RAM" }, { BCMA_CORE_4706_MAC_GBIT, "BCM4706 GBit MAC" }, + { BCMA_CORE_PCIEG2, "PCIe Gen 2" }, + { BCMA_CORE_DMA, "DMA" }, + { BCMA_CORE_SDIO3, "SDIO3" }, + { BCMA_CORE_USB20, "USB 2.0" }, + { BCMA_CORE_USB30, "USB 3.0" }, + { BCMA_CORE_A9JTAG, "ARM Cortex A9 JTAG" }, + { BCMA_CORE_DDR23, "Denali DDR2/DDR3 memory controller" }, + { BCMA_CORE_ROM, "ROM" }, + { BCMA_CORE_NAND, "NAND flash controller" }, + { BCMA_CORE_QSPI, "SPI flash controller" }, + { BCMA_CORE_CHIPCOMMON_B, "Chipcommon B" }, + { BCMA_CORE_ARMCA9, "ARM Cortex A9 core (ihost)" }, { BCMA_CORE_AMEMC, "AMEMC (DDR)" }, { BCMA_CORE_ALTA, "ALTA (I2S)" }, { BCMA_CORE_INVALID, "Invalid" }, diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 622fc505d3e1..8fee02807204 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -72,7 +72,19 @@ struct bcma_host_ops { /* Core-ID values. */ #define BCMA_CORE_OOB_ROUTER 0x367 /* Out of band */ #define BCMA_CORE_4706_CHIPCOMMON 0x500 +#define BCMA_CORE_PCIEG2 0x501 +#define BCMA_CORE_DMA 0x502 +#define BCMA_CORE_SDIO3 0x503 +#define BCMA_CORE_USB20 0x504 +#define BCMA_CORE_USB30 0x505 +#define BCMA_CORE_A9JTAG 0x506 +#define BCMA_CORE_DDR23 0x507 +#define BCMA_CORE_ROM 0x508 +#define BCMA_CORE_NAND 0x509 +#define BCMA_CORE_QSPI 0x50A +#define BCMA_CORE_CHIPCOMMON_B 0x50B #define BCMA_CORE_4706_SOC_RAM 0x50E +#define BCMA_CORE_ARMCA9 0x510 #define BCMA_CORE_4706_MAC_GBIT 0x52D #define BCMA_CORE_AMEMC 0x52E /* DDR1/2 memory controller core */ #define BCMA_CORE_ALTA 0x534 /* I2S core */ -- cgit v1.2.3-71-gd317 From 16041990d1c75efb4408d19413cf4fd27aa148dd Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Mon, 15 Jul 2013 13:15:06 +0200 Subject: bcma: add constants for new ARM based SoCs These are the chipIDs of some ARM based SoCs from the BCM47xx line. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- include/linux/bcma/bcma.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 8fee02807204..4d043c30216f 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -189,6 +189,11 @@ struct bcma_host_ops { #define BCMA_PKG_ID_BCM5357 11 #define BCMA_CHIP_ID_BCM53572 53572 #define BCMA_PKG_ID_BCM47188 9 +#define BCMA_CHIP_ID_BCM4707 53010 +#define BCMA_PKG_ID_BCM4707 1 +#define BCMA_PKG_ID_BCM4708 2 +#define BCMA_PKG_ID_BCM4709 0 +#define BCMA_CHIP_ID_BCM53018 53018 /* Board types (on PCI usually equals to the subsystem dev id) */ /* BCM4313 */ -- cgit v1.2.3-71-gd317 From cb820f8e4b7f73d1a32175e6591735b25bb5398d Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 19 Jul 2013 19:40:09 +0200 Subject: net: Provide a generic socket error queue delivery method for Tx time stamps. This patch moves the private error queue delivery function from the af_packet code to the core socket method. In this way, network layers only needing the error queue for transmit time stamping can share common code. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- include/net/sock.h | 2 ++ net/core/sock.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ net/packet/af_packet.c | 48 ++---------------------------------------------- 3 files changed, 51 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 95a5a2c6925a..e0473f61693f 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2249,6 +2249,8 @@ static inline struct sock *skb_steal_sock(struct sk_buff *skb) extern void sock_enable_timestamp(struct sock *sk, int flag); extern int sock_get_timestamp(struct sock *, struct timeval __user *); extern int sock_get_timestampns(struct sock *, struct timespec __user *); +extern int sock_recv_errqueue(struct sock *sk, struct msghdr *msg, int len, + int level, int type); /* * Enable debug/info messages diff --git a/net/core/sock.c b/net/core/sock.c index 548d716c5f62..85e8de1bc7fd 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -93,6 +93,7 @@ #include #include +#include #include #include #include @@ -2425,6 +2426,52 @@ void sock_enable_timestamp(struct sock *sk, int flag) } } +int sock_recv_errqueue(struct sock *sk, struct msghdr *msg, int len, + int level, int type) +{ + struct sock_exterr_skb *serr; + struct sk_buff *skb, *skb2; + int copied, err; + + err = -EAGAIN; + skb = skb_dequeue(&sk->sk_error_queue); + if (skb == NULL) + goto out; + + copied = skb->len; + if (copied > len) { + msg->msg_flags |= MSG_TRUNC; + copied = len; + } + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); + if (err) + goto out_free_skb; + + sock_recv_timestamp(msg, sk, skb); + + serr = SKB_EXT_ERR(skb); + put_cmsg(msg, level, type, sizeof(serr->ee), &serr->ee); + + msg->msg_flags |= MSG_ERRQUEUE; + err = copied; + + /* Reset and regenerate socket error */ + spin_lock_bh(&sk->sk_error_queue.lock); + sk->sk_err = 0; + if ((skb2 = skb_peek(&sk->sk_error_queue)) != NULL) { + sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno; + spin_unlock_bh(&sk->sk_error_queue.lock); + sk->sk_error_report(sk); + } else + spin_unlock_bh(&sk->sk_error_queue.lock); + +out_free_skb: + kfree_skb(skb); +out: + return err; +} +EXPORT_SYMBOL(sock_recv_errqueue); + /* * Get a socket option on an socket. * diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 4b66c752eae5..4cb28a7f639b 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2638,51 +2638,6 @@ out: return err; } -static int packet_recv_error(struct sock *sk, struct msghdr *msg, int len) -{ - struct sock_exterr_skb *serr; - struct sk_buff *skb, *skb2; - int copied, err; - - err = -EAGAIN; - skb = skb_dequeue(&sk->sk_error_queue); - if (skb == NULL) - goto out; - - copied = skb->len; - if (copied > len) { - msg->msg_flags |= MSG_TRUNC; - copied = len; - } - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); - if (err) - goto out_free_skb; - - sock_recv_timestamp(msg, sk, skb); - - serr = SKB_EXT_ERR(skb); - put_cmsg(msg, SOL_PACKET, PACKET_TX_TIMESTAMP, - sizeof(serr->ee), &serr->ee); - - msg->msg_flags |= MSG_ERRQUEUE; - err = copied; - - /* Reset and regenerate socket error */ - spin_lock_bh(&sk->sk_error_queue.lock); - sk->sk_err = 0; - if ((skb2 = skb_peek(&sk->sk_error_queue)) != NULL) { - sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno; - spin_unlock_bh(&sk->sk_error_queue.lock); - sk->sk_error_report(sk); - } else - spin_unlock_bh(&sk->sk_error_queue.lock); - -out_free_skb: - kfree_skb(skb); -out: - return err; -} - /* * Pull a packet from our receive queue and hand it to the user. * If necessary we block. @@ -2708,7 +2663,8 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, #endif if (flags & MSG_ERRQUEUE) { - err = packet_recv_error(sk, msg, len); + err = sock_recv_errqueue(sk, msg, len, + SOL_PACKET, PACKET_TX_TIMESTAMP); goto out; } -- cgit v1.2.3-71-gd317 From eda297729171fe16bf34fe5b0419dfb69060f623 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Fri, 19 Jul 2013 19:40:10 +0200 Subject: tun: Support software transmit time stamping. This patch adds transmit time stamping to the tun/tap driver. Similar support already exists for UDP, can, and raw packets. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- drivers/net/tun.c | 14 ++++++++++++-- include/uapi/linux/if_tun.h | 3 +++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index db690a372260..a72d141047cb 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -739,6 +739,11 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) >= dev->tx_queue_len / tun->numqueues) goto drop; + if (skb->sk) { + sock_tx_timestamp(skb->sk, &skb_shinfo(skb)->tx_flags); + sw_tx_timestamp(skb); + } + /* Orphan the skb - required as we might hang on to it * for indefinite time. */ if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) @@ -1476,7 +1481,6 @@ static int tun_sendmsg(struct kiocb *iocb, struct socket *sock, return ret; } - static int tun_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t total_len, int flags) @@ -1488,10 +1492,15 @@ static int tun_recvmsg(struct kiocb *iocb, struct socket *sock, if (!tun) return -EBADFD; - if (flags & ~(MSG_DONTWAIT|MSG_TRUNC)) { + if (flags & ~(MSG_DONTWAIT|MSG_TRUNC|MSG_ERRQUEUE)) { ret = -EINVAL; goto out; } + if (flags & MSG_ERRQUEUE) { + ret = sock_recv_errqueue(sock->sk, m, total_len, + SOL_PACKET, TUN_TX_TIMESTAMP); + goto out; + } ret = tun_do_read(tun, tfile, iocb, m->msg_iov, total_len, flags & MSG_DONTWAIT); if (ret > total_len) { @@ -2274,6 +2283,7 @@ static const struct ethtool_ops tun_ethtool_ops = { .get_msglevel = tun_get_msglevel, .set_msglevel = tun_set_msglevel, .get_link = ethtool_op_get_link, + .get_ts_info = ethtool_op_get_ts_info, }; diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h index 82334f88967e..1870ee29bb37 100644 --- a/include/uapi/linux/if_tun.h +++ b/include/uapi/linux/if_tun.h @@ -71,6 +71,9 @@ /* read-only flag */ #define IFF_PERSIST 0x0800 +/* Socket options */ +#define TUN_TX_TIMESTAMP 1 + /* Features for GSO (TUNSETOFFLOAD). */ #define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */ #define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */ -- cgit v1.2.3-71-gd317 From 2c48e3eacb0998dc483860e63da47909313c314e Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 17 Jul 2013 09:48:50 +0800 Subject: ACPICA: Remove restriction of 256 maximum GPEs in any GPE block The FADT can support over 1000 GPEs, so remove any restriction on the GPE numbers. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Acked-by: Len Brown Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/evgpeinit.c | 11 ----------- include/acpi/actypes.h | 7 ------- 2 files changed, 18 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index 9037f17c9608..7842700346a4 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -125,7 +125,6 @@ acpi_status acpi_ev_gpe_initialize(void) /* GPE block 0 exists (has both length and address > 0) */ register_count0 = (u16)(acpi_gbl_FADT.gpe0_block_length / 2); - gpe_number_max = (register_count0 * ACPI_GPE_REGISTER_WIDTH) - 1; @@ -204,16 +203,6 @@ acpi_status acpi_ev_gpe_initialize(void) goto cleanup; } - /* Check for Max GPE number out-of-range */ - - if (gpe_number_max > ACPI_GPE_MAX) { - ACPI_ERROR((AE_INFO, - "Maximum GPE number from FADT is too large: 0x%X", - gpe_number_max)); - status = AE_BAD_VALUE; - goto cleanup; - } - cleanup: (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return_ACPI_STATUS(AE_OK); diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 22b03c9286e9..787a977ece9d 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -668,13 +668,6 @@ typedef u32 acpi_event_status; #define ACPI_EVENT_FLAG_SET (acpi_event_status) 0x04 #define ACPI_EVENT_FLAG_HANDLE (acpi_event_status) 0x08 -/* - * General Purpose Events (GPE) - */ -#define ACPI_GPE_INVALID 0xFF -#define ACPI_GPE_MAX 0xFF -#define ACPI_NUM_GPE 256 - /* Actions for acpi_set_gpe, acpi_gpe_wakeup, acpi_hw_low_set_gpe */ #define ACPI_GPE_ENABLE 0 -- cgit v1.2.3-71-gd317 From 53b1631e3a274f84686853fa2531d1aa918ee36c Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 17 Jul 2013 09:49:08 +0800 Subject: ACPICA: Update version to 20130626 Version 20130626. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Acked-by: Len Brown Signed-off-by: Rafael J. Wysocki --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 22d497ee6ef9..292af0c0b896 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -46,7 +46,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20130517 +#define ACPI_CA_VERSION 0x20130626 #include #include -- cgit v1.2.3-71-gd317 From 375fe02c91792917aa26d68a87ab110d1937f44e Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Mon, 22 Jul 2013 16:20:45 -0700 Subject: tcp: consolidate SYNACK RTT sampling The first patch consolidates SYNACK and other RTT measurement to use a central function tcp_ack_update_rtt(). A (small) bonus is now SYNACK RTT measurement happens after PAWS check, potentially reducing the impact of RTO seeding on bad TCP timestamps values. Signed-off-by: Yuchung Cheng Signed-off-by: David S. Miller --- include/net/tcp.h | 9 --------- net/ipv4/tcp_input.c | 14 ++++++++++++-- net/ipv4/tcp_ipv4.c | 2 -- net/ipv4/tcp_minisocks.c | 8 ++------ net/ipv6/tcp_ipv6.c | 2 -- 5 files changed, 14 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index d1980054ec75..f9777dbede75 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1094,15 +1094,6 @@ static inline void tcp_openreq_init(struct request_sock *req, ireq->loc_port = tcp_hdr(skb)->dest; } -/* Compute time elapsed between SYNACK and the ACK completing 3WHS */ -static inline void tcp_synack_rtt_meas(struct sock *sk, - struct request_sock *req) -{ - if (tcp_rsk(req)->snt_synack) - tcp_valid_rtt_meas(sk, - tcp_time_stamp - tcp_rsk(req)->snt_synack); -} - extern void tcp_enter_memory_pressure(struct sock *sk); static inline int keepalive_intvl_when(const struct tcp_sock *tp) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 28af45abe062..b531710596ec 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2853,6 +2853,17 @@ static inline void tcp_ack_update_rtt(struct sock *sk, const int flag, tcp_ack_no_tstamp(sk, seq_rtt, flag); } +/* Compute time elapsed between (last) SYNACK and the ACK completing 3WHS. */ +static void tcp_synack_rtt_meas(struct sock *sk, struct request_sock *req) +{ + struct tcp_sock *tp = tcp_sk(sk); + s32 seq_rtt = -1; + + if (tp->lsndtime && !tp->total_retrans) + seq_rtt = tcp_time_stamp - tp->lsndtime; + tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt); +} + static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) { const struct inet_connection_sock *icsk = inet_csk(sk); @@ -5624,9 +5635,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, * so release it. */ if (req) { - tcp_synack_rtt_meas(sk, req); tp->total_retrans = req->num_retrans; - reqsk_fastopen_remove(sk, req, false); } else { /* Make sure socket is routed, for correct metrics. */ @@ -5651,6 +5660,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tp->snd_una = TCP_SKB_CB(skb)->ack_seq; tp->snd_wnd = ntohs(th->window) << tp->rx_opt.snd_wscale; tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); + tcp_synack_rtt_meas(sk, req); if (tp->rx_opt.tstamp_ok) tp->advmss -= TCPOLEN_TSTAMP_ALIGNED; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b299da5ff499..2e3f129df0eb 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1671,8 +1671,6 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newtp->advmss = tcp_sk(sk)->rx_opt.user_mss; tcp_initialize_rcv_mss(newsk); - tcp_synack_rtt_meas(newsk, req); - newtp->total_retrans = req->num_retrans; #ifdef CONFIG_TCP_MD5SIG /* Copy over the MD5 key from the original socket */ diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index ab1c08658528..58a3e69aef64 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -411,6 +411,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, newtp->snd_ssthresh = TCP_INFINITE_SSTHRESH; tcp_enable_early_retrans(newtp); newtp->tlp_high_seq = 0; + newtp->lsndtime = treq->snt_synack; + newtp->total_retrans = req->num_retrans; /* So many TCP implementations out there (incorrectly) count the * initial SYN frame in their delayed-ACK and congestion control @@ -666,12 +668,6 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, if (!(flg & TCP_FLAG_ACK)) return NULL; - /* Got ACK for our SYNACK, so update baseline for SYNACK RTT sample. */ - if (tmp_opt.saw_tstamp && tmp_opt.rcv_tsecr) - tcp_rsk(req)->snt_synack = tmp_opt.rcv_tsecr; - else if (req->num_retrans) /* don't take RTT sample if retrans && ~TS */ - tcp_rsk(req)->snt_synack = 0; - /* For Fast Open no more processing is needed (sk is the * child socket). */ diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 6e1649d58533..80fe69ef2188 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1237,8 +1237,6 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newtp->advmss = tcp_sk(sk)->rx_opt.user_mss; tcp_initialize_rcv_mss(newsk); - tcp_synack_rtt_meas(newsk, req); - newtp->total_retrans = req->num_retrans; newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6; newinet->inet_rcv_saddr = LOOPBACK4_IPV6; -- cgit v1.2.3-71-gd317 From 5b08e47caf1f2034a3a5b566bbccc8b0be3961ca Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Mon, 22 Jul 2013 16:20:46 -0700 Subject: tcp: prefer packet timing to TS-ECR for RTT Prefer packet timings to TS-ecr for RTT measurements when both sources are available. That's because broken middle-boxes and remote peer can return packets with corrupted TS ECR fields. Similarly most congestion controls that require RTT signals favor timing-based sources as well. Also check for bad TS ECR values to avoid RTT blow-ups. It has happened on production Web servers. Signed-off-by: Yuchung Cheng Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/tcp.h | 1 - net/ipv4/tcp_input.c | 67 ++++++++++++++-------------------------------------- 2 files changed, 18 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index f9777dbede75..c5868471abae 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -591,7 +591,6 @@ extern void tcp_initialize_rcv_mss(struct sock *sk); extern int tcp_mtu_to_mss(struct sock *sk, int pmtu); extern int tcp_mss_to_mtu(struct sock *sk, int mss); extern void tcp_mtup_init(struct sock *sk); -extern void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt); extern void tcp_init_buffer_space(struct sock *sk); static inline void tcp_bound_rto(const struct sock *sk) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index b531710596ec..c7398f05d12b 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2792,65 +2792,36 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked, tcp_xmit_retransmit_queue(sk); } -void tcp_valid_rtt_meas(struct sock *sk, u32 seq_rtt) +static inline void tcp_ack_update_rtt(struct sock *sk, const int flag, + s32 seq_rtt) { - tcp_rtt_estimator(sk, seq_rtt); - tcp_set_rto(sk); - inet_csk(sk)->icsk_backoff = 0; -} -EXPORT_SYMBOL(tcp_valid_rtt_meas); + const struct tcp_sock *tp = tcp_sk(sk); + + /* Prefer RTT measured from ACK's timing to TS-ECR. This is because + * broken middle-boxes or peers may corrupt TS-ECR fields. But + * Karn's algorithm forbids taking RTT if some retransmitted data + * is acked (RFC6298). + */ + if (flag & FLAG_RETRANS_DATA_ACKED) + seq_rtt = -1; -/* Read draft-ietf-tcplw-high-performance before mucking - * with this code. (Supersedes RFC1323) - */ -static void tcp_ack_saw_tstamp(struct sock *sk, int flag) -{ /* RTTM Rule: A TSecr value received in a segment is used to * update the averaged RTT measurement only if the segment * acknowledges some new data, i.e., only if it advances the * left edge of the send window. - * * See draft-ietf-tcplw-high-performance-00, section 3.3. - * 1998/04/10 Andrey V. Savochkin - * - * Changed: reset backoff as soon as we see the first valid sample. - * If we do not, we get strongly overestimated rto. With timestamps - * samples are accepted even from very old segments: f.e., when rtt=1 - * increases to 8, we retransmit 5 times and after 8 seconds delayed - * answer arrives rto becomes 120 seconds! If at least one of segments - * in window is lost... Voila. --ANK (010210) */ - struct tcp_sock *tp = tcp_sk(sk); + if (seq_rtt < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) + seq_rtt = tcp_time_stamp - tp->rx_opt.rcv_tsecr; - tcp_valid_rtt_meas(sk, tcp_time_stamp - tp->rx_opt.rcv_tsecr); -} - -static void tcp_ack_no_tstamp(struct sock *sk, u32 seq_rtt, int flag) -{ - /* We don't have a timestamp. Can only use - * packets that are not retransmitted to determine - * rtt estimates. Also, we must not reset the - * backoff for rto until we get a non-retransmitted - * packet. This allows us to deal with a situation - * where the network delay has increased suddenly. - * I.e. Karn's algorithm. (SIGCOMM '87, p5.) - */ - - if (flag & FLAG_RETRANS_DATA_ACKED) + if (seq_rtt < 0) return; - tcp_valid_rtt_meas(sk, seq_rtt); -} + tcp_rtt_estimator(sk, seq_rtt); + tcp_set_rto(sk); -static inline void tcp_ack_update_rtt(struct sock *sk, const int flag, - const s32 seq_rtt) -{ - const struct tcp_sock *tp = tcp_sk(sk); - /* Note that peer MAY send zero echo. In this case it is ignored. (rfc1323) */ - if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr) - tcp_ack_saw_tstamp(sk, flag); - else if (seq_rtt >= 0) - tcp_ack_no_tstamp(sk, seq_rtt, flag); + /* RFC6298: only reset backoff on valid RTT measurement. */ + inet_csk(sk)->icsk_backoff = 0; } /* Compute time elapsed between (last) SYNACK and the ACK completing 3WHS. */ @@ -2989,8 +2960,6 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, if (sacked & TCPCB_SACKED_RETRANS) tp->retrans_out -= acked_pcount; flag |= FLAG_RETRANS_DATA_ACKED; - ca_seq_rtt = -1; - seq_rtt = -1; } else { ca_seq_rtt = now - scb->when; last_ackt = skb->tstamp; -- cgit v1.2.3-71-gd317 From be1c9de98d8904c75a5ab8b2a0d97bea0f7c07cc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 13 Jul 2013 23:27:23 +0200 Subject: ACPI / PCI: Make bus registration and unregistration symmetric Since acpi_pci_slot_enumerate() and acpiphp_enumerate_slots() can get the ACPI device handle they need from bus->bridge, it is not necessary to pass that handle to them as an argument. Drop the second argument of acpi_pci_slot_enumerate() and acpiphp_enumerate_slots(), rework them to obtain the ACPI handle from bus->bridge and make acpi_pci_add_bus() and acpi_pci_remove_bus() entirely symmetrical. Tested-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu --- drivers/acpi/pci_slot.c | 14 +++++++++----- drivers/pci/hotplug/acpiphp_glue.c | 6 ++++-- drivers/pci/pci-acpi.c | 16 ++++------------ include/linux/pci-acpi.h | 10 ++++------ 4 files changed, 21 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index 033d1179bdb5..d678a180ca2a 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -159,12 +159,16 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK; } -void acpi_pci_slot_enumerate(struct pci_bus *bus, acpi_handle handle) +void acpi_pci_slot_enumerate(struct pci_bus *bus) { - mutex_lock(&slot_list_lock); - acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, - register_slot, NULL, bus, NULL); - mutex_unlock(&slot_list_lock); + acpi_handle handle = ACPI_HANDLE(bus->bridge); + + if (handle) { + mutex_lock(&slot_list_lock); + acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, + register_slot, NULL, bus, NULL); + mutex_unlock(&slot_list_lock); + } } void acpi_pci_slot_remove(struct pci_bus *bus) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 8bfad0dc29ab..a203ba529fef 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -1147,14 +1147,16 @@ static void handle_hotplug_event_func(acpi_handle handle, u32 type, * Create hotplug slots for the PCI bus. * It should always return 0 to avoid skipping following notifiers. */ -void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle) +void acpiphp_enumerate_slots(struct pci_bus *bus) { + acpi_handle handle; struct acpiphp_bridge *bridge; if (acpiphp_disabled) return; - if (detect_ejectable_slots(handle) <= 0) + handle = ACPI_HANDLE(bus->bridge); + if (!handle || detect_ejectable_slots(handle) <= 0) return; bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index dbdc5f7e2b29..c78cc432edab 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -290,24 +290,16 @@ static struct pci_platform_pm_ops acpi_pci_platform_pm = { void acpi_pci_add_bus(struct pci_bus *bus) { - acpi_handle handle = NULL; - - if (bus->bridge) - handle = ACPI_HANDLE(bus->bridge); - if (acpi_pci_disabled || handle == NULL) + if (acpi_pci_disabled || !bus->bridge) return; - acpi_pci_slot_enumerate(bus, handle); - acpiphp_enumerate_slots(bus, handle); + acpi_pci_slot_enumerate(bus); + acpiphp_enumerate_slots(bus); } void acpi_pci_remove_bus(struct pci_bus *bus) { - /* - * bus->bridge->acpi_node.handle has already been reset to NULL - * when acpi_pci_remove_bus() is called, so don't check ACPI handle. - */ - if (acpi_pci_disabled) + if (acpi_pci_disabled || !bus->bridge) return; acpiphp_remove_slots(bus); diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 170447977278..d006f0ca60f4 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -47,24 +47,22 @@ void acpi_pci_remove_bus(struct pci_bus *bus); #ifdef CONFIG_ACPI_PCI_SLOT void acpi_pci_slot_init(void); -void acpi_pci_slot_enumerate(struct pci_bus *bus, acpi_handle handle); +void acpi_pci_slot_enumerate(struct pci_bus *bus); void acpi_pci_slot_remove(struct pci_bus *bus); #else static inline void acpi_pci_slot_init(void) { } -static inline void acpi_pci_slot_enumerate(struct pci_bus *bus, - acpi_handle handle) { } +static inline void acpi_pci_slot_enumerate(struct pci_bus *bus) { } static inline void acpi_pci_slot_remove(struct pci_bus *bus) { } #endif #ifdef CONFIG_HOTPLUG_PCI_ACPI void acpiphp_init(void); -void acpiphp_enumerate_slots(struct pci_bus *bus, acpi_handle handle); +void acpiphp_enumerate_slots(struct pci_bus *bus); void acpiphp_remove_slots(struct pci_bus *bus); void acpiphp_check_host_bridge(acpi_handle handle); #else static inline void acpiphp_init(void) { } -static inline void acpiphp_enumerate_slots(struct pci_bus *bus, - acpi_handle handle) { } +static inline void acpiphp_enumerate_slots(struct pci_bus *bus) { } static inline void acpiphp_remove_slots(struct pci_bus *bus) { } static inline void acpiphp_check_host_bridge(acpi_handle handle) { } #endif -- cgit v1.2.3-71-gd317 From 2cf9f5bcc8d8cb31d6ea7baebac5056f39fb4f40 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 22 Jul 2013 16:08:16 +0800 Subject: ACPICA: Add acpi_update_interfaces() public interface Add new API to allow OSPM to disable/enable specific types of _OSI interface strings. ACPICA does not have the knowledge about whether an _OSI interface string is an OS vendor string or a feature group string and there isn't any API interface to allow OSPM to install a new interface string as a feature group string. This patch simply adds all feature group strings defined by ACPI specification into the acpi_default_supported_interfaces with ACPI_OSI_FEATURE flag set to fix this gap. This patch also adds codes to keep their default states as ACPI_OSI_INVALID before the initialization and after the termination. Signed-off-by: Lv Zheng Reviewed-by: Zhang Rui Signed-off-by: Bob Moore Acked-by: Len Brown Signed-off-by: Rafael J. Wysocki Conflicts: include/acpi/actypes.h (with commit 242b228) --- drivers/acpi/acpica/aclocal.h | 3 ++ drivers/acpi/acpica/acutils.h | 2 ++ drivers/acpi/acpica/utosi.c | 77 +++++++++++++++++++++++++++++++++++-------- drivers/acpi/acpica/utxface.c | 29 ++++++++++++++++ include/acpi/acpixf.h | 2 ++ include/acpi/actypes.h | 14 ++++++++ 6 files changed, 114 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index d4a4901637cd..b0e3d15cfe96 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -942,6 +942,9 @@ struct acpi_interface_info { #define ACPI_OSI_INVALID 0x01 #define ACPI_OSI_DYNAMIC 0x02 +#define ACPI_OSI_FEATURE 0x04 +#define ACPI_OSI_DEFAULT_INVALID 0x08 +#define ACPI_OSI_OPTIONAL_FEATURE (ACPI_OSI_FEATURE | ACPI_OSI_DEFAULT_INVALID | ACPI_OSI_INVALID) struct acpi_port_info { char *name; diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 4c081c436089..d5a62a6182bb 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -470,6 +470,8 @@ acpi_status acpi_ut_install_interface(acpi_string interface_name); acpi_status acpi_ut_remove_interface(acpi_string interface_name); +acpi_status acpi_ut_update_interfaces(u8 action); + struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name); acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state); diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c index 7e807725c636..8856bd37bc76 100644 --- a/drivers/acpi/acpica/utosi.c +++ b/drivers/acpi/acpica/utosi.c @@ -77,21 +77,20 @@ static struct acpi_interface_info acpi_default_supported_interfaces[] = { /* Feature Group Strings */ - {"Extended Address Space Descriptor", NULL, 0, 0} + {"Extended Address Space Descriptor", NULL, ACPI_OSI_FEATURE, 0}, /* * All "optional" feature group strings (features that are implemented - * by the host) should be dynamically added by the host via - * acpi_install_interface and should not be manually added here. - * - * Examples of optional feature group strings: - * - * "Module Device" - * "Processor Device" - * "3.0 Thermal Model" - * "3.0 _SCP Extensions" - * "Processor Aggregator Device" + * by the host) should be dynamically modified to VALID by the host via + * acpi_install_interface or acpi_update_interfaces. Such optional feature + * group strings are set as INVALID by default here. */ + + {"Module Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, + {"Processor Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, + {"3.0 Thermal Model", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, + {"3.0 _SCP Extensions", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, + {"Processor Aggregator Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0} }; /******************************************************************************* @@ -158,11 +157,20 @@ acpi_status acpi_ut_interface_terminate(void) while (next_interface) { acpi_gbl_supported_interfaces = next_interface->next; - /* Only interfaces added at runtime can be freed */ - if (next_interface->flags & ACPI_OSI_DYNAMIC) { + + /* Only interfaces added at runtime can be freed */ + ACPI_FREE(next_interface->name); ACPI_FREE(next_interface); + } else { + /* Interface is in static list. Reset it to invalid or valid. */ + + if (next_interface->flags & ACPI_OSI_DEFAULT_INVALID) { + next_interface->flags |= ACPI_OSI_INVALID; + } else { + next_interface->flags &= ~ACPI_OSI_INVALID; + } } next_interface = acpi_gbl_supported_interfaces; @@ -276,6 +284,49 @@ acpi_status acpi_ut_remove_interface(acpi_string interface_name) return (AE_NOT_EXIST); } +/******************************************************************************* + * + * FUNCTION: acpi_ut_update_interfaces + * + * PARAMETERS: action - Actions to be performed during the + * update + * + * RETURN: Status + * + * DESCRIPTION: Update _OSI interface strings, disabling or enabling OS vendor + * strings or/and feature group strings. + * Caller MUST hold acpi_gbl_osi_mutex + * + ******************************************************************************/ + +acpi_status acpi_ut_update_interfaces(u8 action) +{ + struct acpi_interface_info *next_interface; + + next_interface = acpi_gbl_supported_interfaces; + while (next_interface) { + if (((next_interface->flags & ACPI_OSI_FEATURE) && + (action & ACPI_FEATURE_STRINGS)) || + (!(next_interface->flags & ACPI_OSI_FEATURE) && + (action & ACPI_VENDOR_STRINGS))) { + if (action & ACPI_DISABLE_INTERFACES) { + + /* Mark the interfaces as invalid */ + + next_interface->flags |= ACPI_OSI_INVALID; + } else { + /* Mark the interfaces as valid */ + + next_interface->flags &= ~ACPI_OSI_INVALID; + } + } + + next_interface = next_interface->next; + } + + return (AE_OK); +} + /******************************************************************************* * * FUNCTION: acpi_ut_get_interface diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index 6505774f223e..03a211e6e26a 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -387,6 +387,34 @@ acpi_status acpi_install_interface_handler(acpi_interface_handler handler) ACPI_EXPORT_SYMBOL(acpi_install_interface_handler) +/***************************************************************************** + * + * FUNCTION: acpi_update_interfaces + * + * PARAMETERS: action - Actions to be performed during the + * update + * + * RETURN: Status + * + * DESCRIPTION: Update _OSI interface strings, disabling or enabling OS vendor + * string or/and feature group strings. + * + ****************************************************************************/ +acpi_status acpi_update_interfaces(u8 action) +{ + acpi_status status; + + status = acpi_os_acquire_mutex(acpi_gbl_osi_mutex, ACPI_WAIT_FOREVER); + if (ACPI_FAILURE(status)) { + return (status); + } + + status = acpi_ut_update_interfaces(action); + + acpi_os_release_mutex(acpi_gbl_osi_mutex); + return (status); +} + /***************************************************************************** * * FUNCTION: acpi_check_address_range @@ -402,6 +430,7 @@ ACPI_EXPORT_SYMBOL(acpi_install_interface_handler) * ASL operation region address ranges. * ****************************************************************************/ + u32 acpi_check_address_range(acpi_adr_space_type space_id, acpi_physical_address address, diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 292af0c0b896..0dd03f226a68 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -147,6 +147,8 @@ acpi_status acpi_install_interface(acpi_string interface_name); acpi_status acpi_remove_interface(acpi_string interface_name); +acpi_status acpi_update_interfaces(u8 action); + u32 acpi_check_address_range(acpi_adr_space_type space_id, acpi_physical_address address, diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 787a977ece9d..eae55fb74905 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -1152,4 +1152,18 @@ struct acpi_memory_list { #define ACPI_OSI_WIN_7 0x0B #define ACPI_OSI_WIN_8 0x0C +/* _OSI update actions */ + +#define ACPI_VENDOR_STRINGS 0x01 +#define ACPI_FEATURE_STRINGS 0x02 +#define ACPI_ENABLE_INTERFACES 0x00 +#define ACPI_DISABLE_INTERFACES 0x04 + +#define ACPI_DISABLE_ALL_VENDOR_STRINGS (ACPI_DISABLE_INTERFACES | ACPI_VENDOR_STRINGS) +#define ACPI_DISABLE_ALL_FEATURE_STRINGS (ACPI_DISABLE_INTERFACES | ACPI_FEATURE_STRINGS) +#define ACPI_DISABLE_ALL_STRINGS (ACPI_DISABLE_INTERFACES | ACPI_VENDOR_STRINGS | ACPI_FEATURE_STRINGS) +#define ACPI_ENABLE_ALL_VENDOR_STRINGS (ACPI_ENABLE_INTERFACES | ACPI_VENDOR_STRINGS) +#define ACPI_ENABLE_ALL_FEATURE_STRINGS (ACPI_ENABLE_INTERFACES | ACPI_FEATURE_STRINGS) +#define ACPI_ENABLE_ALL_STRINGS (ACPI_ENABLE_INTERFACES | ACPI_VENDOR_STRINGS | ACPI_FEATURE_STRINGS) + #endif /* __ACTYPES_H__ */ -- cgit v1.2.3-71-gd317 From 492d774db34fd601b3db17218cf0ae262b51b5cf Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 10 Jul 2013 14:11:35 +0200 Subject: drm: remove drm_modctx ioctl and use drm_noop instead It doesn't do anything, so kill the code. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_context.c | 6 ------ drivers/gpu/drm/drm_drv.c | 2 +- include/drm/drmP.h | 2 -- 3 files changed, 1 insertion(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c index 725968d38976..6f9731ff6a5b 100644 --- a/drivers/gpu/drm/drm_context.c +++ b/drivers/gpu/drm/drm_context.c @@ -342,12 +342,6 @@ int drm_addctx(struct drm_device *dev, void *data, return 0; } -int drm_modctx(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - /* This does nothing */ - return 0; -} - /** * Get context. * diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 99fcd7c32ea2..d668011dafd4 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -87,7 +87,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_addctx, DRM_AUTH|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), - DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_modctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_GET_CTX, drm_getctx, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_SWITCH_CTX, drm_switchctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_NEW_CTX, drm_newctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 12083dc862a9..523e5f2e3218 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1335,8 +1335,6 @@ extern int drm_resctx(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_addctx(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int drm_modctx(struct drm_device *dev, void *data, - struct drm_file *file_priv); extern int drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_switchctx(struct drm_device *dev, void *data, -- cgit v1.2.3-71-gd317 From 3dadef6c96c8aa6e67f83b30504256a0605ee4d6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 10 Jul 2013 14:11:36 +0200 Subject: drm: kill dev->context_wait No one ever waits on this waitqueue, so the wake_up call is wasted. Remove it all. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_context.c | 1 - drivers/gpu/drm/drm_fops.c | 1 - include/drm/drmP.h | 1 - 3 files changed, 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c index 6f9731ff6a5b..5cc17f32eb79 100644 --- a/drivers/gpu/drm/drm_context.c +++ b/drivers/gpu/drm/drm_context.c @@ -261,7 +261,6 @@ static int drm_context_switch_complete(struct drm_device *dev, when the kernel holds the lock, release that lock here. */ clear_bit(0, &dev->context_flag); - wake_up(&dev->context_wait); return 0; } diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 3a24385e0368..0dcbbdb6f972 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -81,7 +81,6 @@ static int drm_setup(struct drm_device * dev) dev->last_context = 0; dev->last_switch = 0; dev->last_checked = 0; - init_waitqueue_head(&dev->context_wait); dev->if_version = 0; dev->ctx_start = 0; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 523e5f2e3218..449913313100 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1133,7 +1133,6 @@ struct drm_device { __volatile__ long context_flag; /**< Context swapping flag */ __volatile__ long interrupt_flag; /**< Interruption handler flag */ __volatile__ long dma_flag; /**< DMA dispatch flag */ - wait_queue_head_t context_wait; /**< Processes waiting on ctx switch */ int last_checked; /**< Last context checked for DMA */ int last_context; /**< Last current context */ unsigned long last_switch; /**< jiffies at last context switch */ -- cgit v1.2.3-71-gd317 From a17800c70129d5976a52c42f04a16a0f1d9df4b2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 10 Jul 2013 14:11:37 +0200 Subject: drm: remove dev->last_switch Only ever assigned in the context code for real, with no readers anywhere. Remove it. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_context.c | 1 - drivers/gpu/drm/drm_fops.c | 1 - include/drm/drmP.h | 1 - 3 files changed, 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c index 5cc17f32eb79..224ff965bcf7 100644 --- a/drivers/gpu/drm/drm_context.c +++ b/drivers/gpu/drm/drm_context.c @@ -251,7 +251,6 @@ static int drm_context_switch_complete(struct drm_device *dev, struct drm_file *file_priv, int new) { dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ - dev->last_switch = jiffies; if (!_DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock)) { DRM_ERROR("Lock isn't held after context switch\n"); diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 0dcbbdb6f972..c14fdc1c109b 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -79,7 +79,6 @@ static int drm_setup(struct drm_device * dev) dev->interrupt_flag = 0; dev->dma_flag = 0; dev->last_context = 0; - dev->last_switch = 0; dev->last_checked = 0; dev->if_version = 0; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 449913313100..089e82028e7a 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1135,7 +1135,6 @@ struct drm_device { __volatile__ long dma_flag; /**< DMA dispatch flag */ int last_checked; /**< Last context checked for DMA */ int last_context; /**< Last current context */ - unsigned long last_switch; /**< jiffies at last context switch */ /*@} */ struct work_struct work; -- cgit v1.2.3-71-gd317 From c78d7531031cb6d163e7450bda563c267beef777 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 10 Jul 2013 14:11:38 +0200 Subject: drm: kill dev->interrupt_flag and dev->dma_flag Completely unused, so just remove them. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_fops.c | 2 -- include/drm/drmP.h | 2 -- 2 files changed, 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index c14fdc1c109b..386c304c5cb1 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -76,8 +76,6 @@ static int drm_setup(struct drm_device * dev) dev->sigdata.lock = NULL; dev->context_flag = 0; - dev->interrupt_flag = 0; - dev->dma_flag = 0; dev->last_context = 0; dev->last_checked = 0; dev->if_version = 0; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 089e82028e7a..1ec806f76ad9 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1131,8 +1131,6 @@ struct drm_device { /*@{ */ int irq_enabled; /**< True if irq handler is enabled */ __volatile__ long context_flag; /**< Context swapping flag */ - __volatile__ long interrupt_flag; /**< Interruption handler flag */ - __volatile__ long dma_flag; /**< DMA dispatch flag */ int last_checked; /**< Last context checked for DMA */ int last_context; /**< Last current context */ /*@} */ -- cgit v1.2.3-71-gd317 From c7e00b6d6a08772fac43b0fcea7fb48e6a1fe390 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 10 Jul 2013 14:11:39 +0200 Subject: drm: kill dev->ctx_start and dev->lck_start Again completely unused, so just remove it. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_fops.c | 3 --- include/drm/drmP.h | 2 -- 2 files changed, 5 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 386c304c5cb1..a3714a0235cb 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -80,9 +80,6 @@ static int drm_setup(struct drm_device * dev) dev->last_checked = 0; dev->if_version = 0; - dev->ctx_start = 0; - dev->lck_start = 0; - dev->buf_async = NULL; init_waitqueue_head(&dev->buf_readers); init_waitqueue_head(&dev->buf_writers); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 1ec806f76ad9..040369571504 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1170,8 +1170,6 @@ struct drm_device { spinlock_t event_lock; /*@} */ - cycles_t ctx_start; - cycles_t lck_start; struct fasync_struct *buf_async;/**< Processes waiting for SIGIO */ wait_queue_head_t buf_readers; /**< Processes waiting to read */ -- cgit v1.2.3-71-gd317 From 494f38e4e0c5ffca110e361cd3391f25313b52c7 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 10 Jul 2013 14:11:41 +0200 Subject: drm: kill dev->buf_readers and dev->buf_writers Again totally unused, so just remove them. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_fops.c | 2 -- include/drm/drmP.h | 2 -- 2 files changed, 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index a3714a0235cb..57e30145f68e 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -81,8 +81,6 @@ static int drm_setup(struct drm_device * dev) dev->if_version = 0; dev->buf_async = NULL; - init_waitqueue_head(&dev->buf_readers); - init_waitqueue_head(&dev->buf_writers); DRM_DEBUG("\n"); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 040369571504..6cde92465130 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1172,8 +1172,6 @@ struct drm_device { /*@} */ struct fasync_struct *buf_async;/**< Processes waiting for SIGIO */ - wait_queue_head_t buf_readers; /**< Processes waiting to read */ - wait_queue_head_t buf_writers; /**< Processes waiting to ctx switch */ struct drm_agp_head *agp; /**< AGP data */ -- cgit v1.2.3-71-gd317 From 1d8d29cf2a9956ec5ea2395232a8121577a6cfee Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 10 Jul 2013 14:11:50 +0200 Subject: drm: fold in drm_sg_alloc into the ioctl There's no other caller from driver code, so we can fold this in. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_drv.c | 2 +- drivers/gpu/drm/drm_scatter.c | 13 +++---------- include/drm/drmP.h | 3 +-- 3 files changed, 5 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index d668011dafd4..5993bfc8dcb9 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -122,7 +122,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_AGP_UNBIND, drm_agp_unbind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), #endif - DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_sg_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), + DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_sg_alloc, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, DRM_UNLOCKED), diff --git a/drivers/gpu/drm/drm_scatter.c b/drivers/gpu/drm/drm_scatter.c index d87f60bbc330..a4a076ff1757 100644 --- a/drivers/gpu/drm/drm_scatter.c +++ b/drivers/gpu/drm/drm_scatter.c @@ -70,8 +70,10 @@ void drm_sg_cleanup(struct drm_sg_mem * entry) # define ScatterHandle(x) (unsigned int)(x) #endif -int drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather * request) +int drm_sg_alloc(struct drm_device *dev, void *data, + struct drm_file *file_priv) { + struct drm_scatter_gather *request = data; struct drm_sg_mem *entry; unsigned long pages, i, j; @@ -181,15 +183,6 @@ int drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather * request) return -ENOMEM; } -int drm_sg_alloc_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_scatter_gather *request = data; - - return drm_sg_alloc(dev, request); - -} - int drm_sg_free(struct drm_device *dev, void *data, struct drm_file *file_priv) { diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 6cde92465130..1ea404fd745d 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1567,9 +1567,8 @@ extern int drm_vma_info(struct seq_file *m, void *data); /* Scatter Gather Support (drm_scatter.h) */ extern void drm_sg_cleanup(struct drm_sg_mem * entry); -extern int drm_sg_alloc_ioctl(struct drm_device *dev, void *data, +extern int drm_sg_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather * request); extern int drm_sg_free(struct drm_device *dev, void *data, struct drm_file *file_priv); -- cgit v1.2.3-71-gd317 From 23367ff49065505e4a255dba2117f654ca26063f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 10 Jul 2013 17:51:10 +0200 Subject: drm: rip out dev->last_checked Only ever re-cleared in drm_setup, otherwise completely unused. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_fops.c | 1 - include/drm/drmP.h | 1 - 2 files changed, 2 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 57e30145f68e..72acae908a7d 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -77,7 +77,6 @@ static int drm_setup(struct drm_device * dev) dev->context_flag = 0; dev->last_context = 0; - dev->last_checked = 0; dev->if_version = 0; dev->buf_async = NULL; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 1ea404fd745d..dd03fd4239ad 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1131,7 +1131,6 @@ struct drm_device { /*@{ */ int irq_enabled; /**< True if irq handler is enabled */ __volatile__ long context_flag; /**< Context swapping flag */ - int last_checked; /**< Last context checked for DMA */ int last_context; /**< Last current context */ /*@} */ -- cgit v1.2.3-71-gd317 From 89c8233f82d9c8af5b20e72e4a185a38a7d3c50b Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 11 Jul 2013 11:56:32 +0200 Subject: drm/gem: simplify object initialization drm_gem_object_init() and drm_gem_private_object_init() do exactly the same (except for shmem alloc) so make the first use the latter to reduce code duplication. Also drop the return code from drm_gem_private_object_init(). It seems unlikely that we will extend it any time soon so no reason to keep it around. This simplifies code paths in drivers, too. Last but not least, fix gma500 to call drm_gem_object_release() before freeing objects that were allocated via drm_gem_private_object_init(). That isn't actually necessary for now, but might be in the future. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter Reviewed-by: Patrik Jakobsson Acked-by: Rob Clark Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 20 ++++++++------------ drivers/gpu/drm/gma500/framebuffer.c | 6 ++---- drivers/gpu/drm/gma500/gem.c | 7 ++++--- drivers/gpu/drm/i915/i915_gem_dmabuf.c | 7 +------ drivers/gpu/drm/i915/i915_gem_stolen.c | 4 +--- drivers/gpu/drm/omapdrm/omap_gem.c | 3 ++- include/drm/drmP.h | 4 ++-- 7 files changed, 20 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 603f256152ef..1ad9e7ec0119 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -132,16 +132,14 @@ drm_gem_destroy(struct drm_device *dev) int drm_gem_object_init(struct drm_device *dev, struct drm_gem_object *obj, size_t size) { - BUG_ON((size & (PAGE_SIZE - 1)) != 0); + struct file *filp; - obj->dev = dev; - obj->filp = shmem_file_setup("drm mm object", size, VM_NORESERVE); - if (IS_ERR(obj->filp)) - return PTR_ERR(obj->filp); + filp = shmem_file_setup("drm mm object", size, VM_NORESERVE); + if (IS_ERR(filp)) + return PTR_ERR(filp); - kref_init(&obj->refcount); - atomic_set(&obj->handle_count, 0); - obj->size = size; + drm_gem_private_object_init(dev, obj, size); + obj->filp = filp; return 0; } @@ -152,8 +150,8 @@ EXPORT_SYMBOL(drm_gem_object_init); * no GEM provided backing store. Instead the caller is responsible for * backing the object and handling it. */ -int drm_gem_private_object_init(struct drm_device *dev, - struct drm_gem_object *obj, size_t size) +void drm_gem_private_object_init(struct drm_device *dev, + struct drm_gem_object *obj, size_t size) { BUG_ON((size & (PAGE_SIZE - 1)) != 0); @@ -163,8 +161,6 @@ int drm_gem_private_object_init(struct drm_device *dev, kref_init(&obj->refcount); atomic_set(&obj->handle_count, 0); obj->size = size; - - return 0; } EXPORT_SYMBOL(drm_gem_private_object_init); diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 8b1b6d923abe..362dd2ad286f 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -321,10 +321,8 @@ static struct gtt_range *psbfb_alloc(struct drm_device *dev, int aligned_size) /* Begin by trying to use stolen memory backing */ backing = psb_gtt_alloc_range(dev, aligned_size, "fb", 1); if (backing) { - if (drm_gem_private_object_init(dev, - &backing->gem, aligned_size) == 0) - return backing; - psb_gtt_free_range(dev, backing); + drm_gem_private_object_init(dev, &backing->gem, aligned_size); + return backing; } return NULL; } diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c index eefd6cc5b80d..fe1d3320ce6a 100644 --- a/drivers/gpu/drm/gma500/gem.c +++ b/drivers/gpu/drm/gma500/gem.c @@ -261,11 +261,12 @@ static int psb_gem_create_stolen(struct drm_file *file, struct drm_device *dev, struct gtt_range *gtt = psb_gtt_alloc_range(dev, size, "gem", 1); if (gtt == NULL) return -ENOMEM; - if (drm_gem_private_object_init(dev, >t->gem, size) != 0) - goto free_gtt; + + drm_gem_private_object_init(dev, >t->gem, size); if (drm_gem_handle_create(file, >t->gem, handle) == 0) return 0; -free_gtt: + + drm_gem_object_release(>t->gem); psb_gtt_free_range(dev, gtt); return -ENOMEM; } diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index dc53a527126b..f2e185c9038f 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -289,12 +289,7 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev, goto fail_detach; } - ret = drm_gem_private_object_init(dev, &obj->base, dma_buf->size); - if (ret) { - i915_gem_object_free(obj); - goto fail_detach; - } - + drm_gem_private_object_init(dev, &obj->base, dma_buf->size); i915_gem_object_init(obj, &i915_gem_object_dmabuf_ops); obj->base.import_attach = attach; diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 5c1a535d5072..55218332e625 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -291,9 +291,7 @@ _i915_gem_object_create_stolen(struct drm_device *dev, if (obj == NULL) return NULL; - if (drm_gem_private_object_init(dev, &obj->base, stolen->size)) - goto cleanup; - + drm_gem_private_object_init(dev, &obj->base, stolen->size); i915_gem_object_init(obj, &i915_gem_object_stolen_ops); obj->pages = i915_pages_create_for_stolen(dev, diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index ebbdf4132e9c..cbcd71e6ed83 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -1427,8 +1427,9 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, omap_obj->height = gsize.tiled.height; } + ret = 0; if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM)) - ret = drm_gem_private_object_init(dev, obj, size); + drm_gem_private_object_init(dev, obj, size); else ret = drm_gem_object_init(dev, obj, size); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index dd03fd4239ad..86f524338e03 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1601,8 +1601,8 @@ struct drm_gem_object *drm_gem_object_alloc(struct drm_device *dev, size_t size); int drm_gem_object_init(struct drm_device *dev, struct drm_gem_object *obj, size_t size); -int drm_gem_private_object_init(struct drm_device *dev, - struct drm_gem_object *obj, size_t size); +void drm_gem_private_object_init(struct drm_device *dev, + struct drm_gem_object *obj, size_t size); void drm_gem_object_handle_free(struct drm_gem_object *obj); void drm_gem_vm_open(struct vm_area_struct *vma); void drm_gem_vm_close(struct vm_area_struct *vma); -- cgit v1.2.3-71-gd317 From da5cbe361c55fe17ef94d2587991997f81f8c4cb Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 16 Jul 2013 09:11:56 +0200 Subject: drm/gem: remove drm_gem_object_handle_unreference It's unused, everyone is using the _unlocked variant only. Signed-off-by: Daniel Vetter Reviewed-by: Rob Clark Signed-off-by: Dave Airlie --- include/drm/drmP.h | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'include') diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 86f524338e03..5664acea3000 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1648,24 +1648,6 @@ drm_gem_object_handle_reference(struct drm_gem_object *obj) atomic_inc(&obj->handle_count); } -static inline void -drm_gem_object_handle_unreference(struct drm_gem_object *obj) -{ - if (obj == NULL) - return; - - if (atomic_read(&obj->handle_count) == 0) - return; - /* - * Must bump handle count first as this may be the last - * ref, in which case the object would disappear before we - * checked for a name - */ - if (atomic_dec_and_test(&obj->handle_count)) - drm_gem_object_handle_free(obj); - drm_gem_object_unreference(obj); -} - static inline void drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) { -- cgit v1.2.3-71-gd317 From 85d9cb41db3bf0f36c999c2e547b37cb9f32367b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 10 Jul 2013 14:12:00 +0200 Subject: drm: remove drm_order All users of it are now gone! Signed-off-by: Daniel Vetter Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_bufs.c | 23 ----------------------- include/drm/drmP.h | 1 - 2 files changed, 24 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index c5bbcaaa73c0..bef4abff8fa3 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -1613,26 +1613,3 @@ struct drm_local_map *drm_getsarea(struct drm_device *dev) return NULL; } EXPORT_SYMBOL(drm_getsarea); - -/** - * Compute size order. Returns the exponent of the smaller power of two which - * is greater or equal to given number. - * - * \param size size. - * \return order. - * - * \todo Can be made faster. - */ -int drm_order(unsigned long size) -{ - int order; - unsigned long tmp; - - for (order = 0, tmp = size >> 1; tmp; tmp >>= 1, order++) ; - - if (size & (size - 1)) - ++order; - - return order; -} -EXPORT_SYMBOL(drm_order); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 5664acea3000..0ab6a090a15c 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1394,7 +1394,6 @@ extern int drm_freebufs(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mapbufs(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int drm_order(unsigned long size); /* DMA support (drm_dma.h) */ extern int drm_dma_setup(struct drm_device *dev); -- cgit v1.2.3-71-gd317 From a5cdd40c9877e9aba704c020fd65d26b5cfecf18 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 16 Jul 2013 17:09:07 +0200 Subject: perf: Update perf_event_type documentation Due to a discussion with Adrian I had a good look at the perf_event_type record layout and found the documentation to be somewhat unclear. Cc: Adrian Hunter Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/20130716150907.GL23818@dyad.programming.kicks-ass.net Signed-off-by: Ingo Molnar --- include/uapi/linux/perf_event.h | 18 +++++++++++++++++- kernel/events/core.c | 31 ++++++++++++++++--------------- 2 files changed, 33 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 0b1df41691e8..00d8274730b4 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -478,6 +478,16 @@ enum perf_event_type { * file will be supported by older perf tools, with these new optional * fields being ignored. * + * struct sample_id { + * { u32 pid, tid; } && PERF_SAMPLE_TID + * { u64 time; } && PERF_SAMPLE_TIME + * { u64 id; } && PERF_SAMPLE_ID + * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID + * { u32 cpu, res; } && PERF_SAMPLE_CPU + * } && perf_event_attr::sample_id_all + */ + + /* * The MMAP events record the PROT_EXEC mappings so that we can * correlate userspace IPs to code. They have the following structure: * @@ -498,6 +508,7 @@ enum perf_event_type { * struct perf_event_header header; * u64 id; * u64 lost; + * struct sample_id sample_id; * }; */ PERF_RECORD_LOST = 2, @@ -508,6 +519,7 @@ enum perf_event_type { * * u32 pid, tid; * char comm[]; + * struct sample_id sample_id; * }; */ PERF_RECORD_COMM = 3, @@ -518,6 +530,7 @@ enum perf_event_type { * u32 pid, ppid; * u32 tid, ptid; * u64 time; + * struct sample_id sample_id; * }; */ PERF_RECORD_EXIT = 4, @@ -528,6 +541,7 @@ enum perf_event_type { * u64 time; * u64 id; * u64 stream_id; + * struct sample_id sample_id; * }; */ PERF_RECORD_THROTTLE = 5, @@ -539,6 +553,7 @@ enum perf_event_type { * u32 pid, ppid; * u32 tid, ptid; * u64 time; + * struct sample_id sample_id; * }; */ PERF_RECORD_FORK = 7, @@ -549,6 +564,7 @@ enum perf_event_type { * u32 pid, tid; * * struct read_format values; + * struct sample_id sample_id; * }; */ PERF_RECORD_READ = 8, @@ -596,7 +612,7 @@ enum perf_event_type { * u64 dyn_size; } && PERF_SAMPLE_STACK_USER * * { u64 weight; } && PERF_SAMPLE_WEIGHT - * { u64 data_src; } && PERF_SAMPLE_DATA_SRC + * { u64 data_src; } && PERF_SAMPLE_DATA_SRC * }; */ PERF_RECORD_SAMPLE = 9, diff --git a/kernel/events/core.c b/kernel/events/core.c index 5e2bce90b477..127411400116 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4462,20 +4462,6 @@ void perf_output_sample(struct perf_output_handle *handle, } } - if (!event->attr.watermark) { - int wakeup_events = event->attr.wakeup_events; - - if (wakeup_events) { - struct ring_buffer *rb = handle->rb; - int events = local_inc_return(&rb->events); - - if (events >= wakeup_events) { - local_sub(wakeup_events, &rb->events); - local_inc(&rb->wakeup); - } - } - } - if (sample_type & PERF_SAMPLE_BRANCH_STACK) { if (data->br_stack) { size_t size; @@ -4511,16 +4497,31 @@ void perf_output_sample(struct perf_output_handle *handle, } } - if (sample_type & PERF_SAMPLE_STACK_USER) + if (sample_type & PERF_SAMPLE_STACK_USER) { perf_output_sample_ustack(handle, data->stack_user_size, data->regs_user.regs); + } if (sample_type & PERF_SAMPLE_WEIGHT) perf_output_put(handle, data->weight); if (sample_type & PERF_SAMPLE_DATA_SRC) perf_output_put(handle, data->data_src.val); + + if (!event->attr.watermark) { + int wakeup_events = event->attr.wakeup_events; + + if (wakeup_events) { + struct ring_buffer *rb = handle->rb; + int events = local_inc_return(&rb->events); + + if (events >= wakeup_events) { + local_sub(wakeup_events, &rb->events); + local_inc(&rb->wakeup); + } + } + } } void perf_prepare_sample(struct perf_event_header *header, -- cgit v1.2.3-71-gd317 From 860f085b74e9f0075de8140ed3a1e5b5e3e39aa8 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 28 Jun 2013 16:22:17 +0300 Subject: perf: Fix broken union in 'struct perf_event_mmap_page' The capabilities bits must not be "union'ed" together. Put them in a separate struct. Signed-off-by: Adrian Hunter Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/1372425741-1676-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Ingo Molnar --- include/uapi/linux/perf_event.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 00d8274730b4..0041aedf2297 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -375,9 +375,11 @@ struct perf_event_mmap_page { __u64 time_running; /* time event on cpu */ union { __u64 capabilities; - __u64 cap_usr_time : 1, - cap_usr_rdpmc : 1, - cap_____res : 62; + struct { + __u64 cap_usr_time : 1, + cap_usr_rdpmc : 1, + cap_____res : 62; + }; }; /* -- cgit v1.2.3-71-gd317 From c73deb6aecda2955716f31572516f09d930ef450 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 28 Jun 2013 16:22:18 +0300 Subject: perf/x86: Add ability to calculate TSC from perf sample timestamps For modern CPUs, perf clock is directly related to TSC. TSC can be calculated from perf clock and vice versa using a simple calculation. Two of the three componenets of that calculation are already exported in struct perf_event_mmap_page. This patch exports the third. Signed-off-by: Adrian Hunter Signed-off-by: Peter Zijlstra Cc: "H. Peter Anvin" Link: http://lkml.kernel.org/r/1372425741-1676-3-git-send-email-adrian.hunter@intel.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/tsc.h | 1 + arch/x86/kernel/cpu/perf_event.c | 6 ++++++ arch/x86/kernel/tsc.c | 6 ++++++ include/uapi/linux/perf_event.h | 22 ++++++++++++++++++++-- 4 files changed, 33 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/tsc.h b/arch/x86/include/asm/tsc.h index c91e8b9d588b..235be70d5bb4 100644 --- a/arch/x86/include/asm/tsc.h +++ b/arch/x86/include/asm/tsc.h @@ -49,6 +49,7 @@ extern void tsc_init(void); extern void mark_tsc_unstable(char *reason); extern int unsynchronized_tsc(void); extern int check_tsc_unstable(void); +extern int check_tsc_disabled(void); extern unsigned long native_calibrate_tsc(void); extern int tsc_clocksource_reliable; diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index a7c7305030cc..8355c84b9729 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1884,6 +1884,7 @@ static struct pmu pmu = { void arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now) { userpg->cap_usr_time = 0; + userpg->cap_usr_time_zero = 0; userpg->cap_usr_rdpmc = x86_pmu.attr_rdpmc; userpg->pmc_width = x86_pmu.cntval_bits; @@ -1897,6 +1898,11 @@ void arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now) userpg->time_mult = this_cpu_read(cyc2ns); userpg->time_shift = CYC2NS_SCALE_FACTOR; userpg->time_offset = this_cpu_read(cyc2ns_offset) - now; + + if (sched_clock_stable && !check_tsc_disabled()) { + userpg->cap_usr_time_zero = 1; + userpg->time_zero = this_cpu_read(cyc2ns_offset); + } } /* diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c index 6ff49247edf8..930e5d48f560 100644 --- a/arch/x86/kernel/tsc.c +++ b/arch/x86/kernel/tsc.c @@ -89,6 +89,12 @@ int check_tsc_unstable(void) } EXPORT_SYMBOL_GPL(check_tsc_unstable); +int check_tsc_disabled(void) +{ + return tsc_disabled; +} +EXPORT_SYMBOL_GPL(check_tsc_disabled); + #ifdef CONFIG_X86_TSC int __init notsc_setup(char *str) { diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 0041aedf2297..efef1d37a371 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -378,7 +378,8 @@ struct perf_event_mmap_page { struct { __u64 cap_usr_time : 1, cap_usr_rdpmc : 1, - cap_____res : 62; + cap_usr_time_zero : 1, + cap_____res : 61; }; }; @@ -420,12 +421,29 @@ struct perf_event_mmap_page { __u16 time_shift; __u32 time_mult; __u64 time_offset; + /* + * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated + * from sample timestamps. + * + * time = timestamp - time_zero; + * quot = time / time_mult; + * rem = time % time_mult; + * cyc = (quot << time_shift) + (rem << time_shift) / time_mult; + * + * And vice versa: + * + * quot = cyc >> time_shift; + * rem = cyc & ((1 << time_shift) - 1); + * timestamp = time_zero + quot * time_mult + + * ((rem * time_mult) >> time_shift); + */ + __u64 time_zero; /* * Hole for extension of the self monitor capabilities */ - __u64 __reserved[120]; /* align to 1k */ + __u64 __reserved[119]; /* align to 1k */ /* * Control data for the mmap() data buffer. -- cgit v1.2.3-71-gd317 From 62470419e993f8d9d93db0effd3af4296ecb79a5 Mon Sep 17 00:00:00 2001 From: Michael Wang Date: Thu, 4 Jul 2013 12:55:51 +0800 Subject: sched: Implement smarter wake-affine logic The wake-affine scheduler feature is currently always trying to pull the wakee close to the waker. In theory this should be beneficial if the waker's CPU caches hot data for the wakee, and it's also beneficial in the extreme ping-pong high context switch rate case. Testing shows it can benefit hackbench up to 15%. However, the feature is somewhat blind, from which some workloads such as pgbench suffer. It's also time-consuming algorithmically. Testing shows it can damage pgbench up to 50% - far more than the benefit it brings in the best case. So wake-affine should be smarter and it should realize when to stop its thankless effort at trying to find a suitable CPU to wake on. This patch introduces 'wakee_flips', which will be increased each time the task flips (switches) its wakee target. So a high 'wakee_flips' value means the task has more than one wakee, and the bigger the number, the higher the wakeup frequency. Now when making the decision on whether to pull or not, pay attention to the wakee with a high 'wakee_flips', pulling such a task may benefit the wakee. Also imply that the waker will face cruel competition later, it could be very cruel or very fast depends on the story behind 'wakee_flips', waker therefore suffers. Furthermore, if waker also has a high 'wakee_flips', that implies that multiple tasks rely on it, then waker's higher latency will damage all of them, so pulling wakee seems to be a bad deal. Thus, when 'waker->wakee_flips / wakee->wakee_flips' becomes higher and higher, the cost of pulling seems to be worse and worse. The patch therefore helps the wake-affine feature to stop its pulling work when: wakee->wakee_flips > factor && waker->wakee_flips > (factor * wakee->wakee_flips) The 'factor' here is the number of CPUs in the current CPU's NUMA node, so a bigger node will lead to more pulling since the trial becomes more severe. After applying the patch, pgbench shows up to 40% improvements and no regressions. Tested with 12 cpu x86 server and tip 3.10.0-rc7. The percentages in the final column highlight the areas with the biggest wins, all other areas improved as well: pgbench base smart | db_size | clients | tps | | tps | +---------+---------+-------+ +-------+ | 22 MB | 1 | 10598 | | 10796 | | 22 MB | 2 | 21257 | | 21336 | | 22 MB | 4 | 41386 | | 41622 | | 22 MB | 8 | 51253 | | 57932 | | 22 MB | 12 | 48570 | | 54000 | | 22 MB | 16 | 46748 | | 55982 | +19.75% | 22 MB | 24 | 44346 | | 55847 | +25.93% | 22 MB | 32 | 43460 | | 54614 | +25.66% | 7484 MB | 1 | 8951 | | 9193 | | 7484 MB | 2 | 19233 | | 19240 | | 7484 MB | 4 | 37239 | | 37302 | | 7484 MB | 8 | 46087 | | 50018 | | 7484 MB | 12 | 42054 | | 48763 | | 7484 MB | 16 | 40765 | | 51633 | +26.66% | 7484 MB | 24 | 37651 | | 52377 | +39.11% | 7484 MB | 32 | 37056 | | 51108 | +37.92% | 15 GB | 1 | 8845 | | 9104 | | 15 GB | 2 | 19094 | | 19162 | | 15 GB | 4 | 36979 | | 36983 | | 15 GB | 8 | 46087 | | 49977 | | 15 GB | 12 | 41901 | | 48591 | | 15 GB | 16 | 40147 | | 50651 | +26.16% | 15 GB | 24 | 37250 | | 52365 | +40.58% | 15 GB | 32 | 36470 | | 50015 | +37.14% Signed-off-by: Michael Wang Cc: Mike Galbraith Signed-off-by: Peter Zijlstra Link: http://lkml.kernel.org/r/51D50057.9000809@linux.vnet.ibm.com [ Improved the changelog. ] Signed-off-by: Ingo Molnar --- include/linux/sched.h | 3 +++ kernel/sched/fair.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 50d04b92ceda..4f163a8ffabf 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1034,6 +1034,9 @@ struct task_struct { #ifdef CONFIG_SMP struct llist_node wake_entry; int on_cpu; + struct task_struct *last_wakee; + unsigned long wakee_flips; + unsigned long wakee_flip_decay_ts; #endif int on_rq; diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 765d87acdf05..860063a8c849 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3017,6 +3017,23 @@ static unsigned long cpu_avg_load_per_task(int cpu) return 0; } +static void record_wakee(struct task_struct *p) +{ + /* + * Rough decay (wiping) for cost saving, don't worry + * about the boundary, really active task won't care + * about the loss. + */ + if (jiffies > current->wakee_flip_decay_ts + HZ) { + current->wakee_flips = 0; + current->wakee_flip_decay_ts = jiffies; + } + + if (current->last_wakee != p) { + current->last_wakee = p; + current->wakee_flips++; + } +} static void task_waking_fair(struct task_struct *p) { @@ -3037,6 +3054,7 @@ static void task_waking_fair(struct task_struct *p) #endif se->vruntime -= min_vruntime; + record_wakee(p); } #ifdef CONFIG_FAIR_GROUP_SCHED @@ -3155,6 +3173,28 @@ static inline unsigned long effective_load(struct task_group *tg, int cpu, #endif +static int wake_wide(struct task_struct *p) +{ + int factor = nr_cpus_node(cpu_to_node(smp_processor_id())); + + /* + * Yeah, it's the switching-frequency, could means many wakee or + * rapidly switch, use factor here will just help to automatically + * adjust the loose-degree, so bigger node will lead to more pull. + */ + if (p->wakee_flips > factor) { + /* + * wakee is somewhat hot, it needs certain amount of cpu + * resource, so if waker is far more hot, prefer to leave + * it alone. + */ + if (current->wakee_flips > (factor * p->wakee_flips)) + return 1; + } + + return 0; +} + static int wake_affine(struct sched_domain *sd, struct task_struct *p, int sync) { s64 this_load, load; @@ -3164,6 +3204,13 @@ static int wake_affine(struct sched_domain *sd, struct task_struct *p, int sync) unsigned long weight; int balanced; + /* + * If we wake multiple tasks be careful to not bounce + * ourselves around too much. + */ + if (wake_wide(p)) + return 0; + idx = sd->wake_idx; this_cpu = smp_processor_id(); prev_cpu = task_cpu(p); -- cgit v1.2.3-71-gd317 From 2753e6f8207355ab72574d48287cec187272a151 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 22 Jul 2013 17:15:52 +0200 Subject: regmap: irq: Allow to acknowledge masked interrupts during initialization In case the hardware interrupt mask register does not prevent the chip level irq from being asserted by the corresponding interrupt status bit, already set interrupt bits should to be cleared once after masking them during initialization. Add a flag to let drivers enable this behavior. Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 25 +++++++++++++++++++++++++ include/linux/regmap.h | 2 ++ 2 files changed, 27 insertions(+) (limited to 'include') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 1643e889bafc..d10456ffd811 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -418,6 +418,31 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, reg, ret); goto err_alloc; } + + if (!chip->init_ack_masked) + continue; + + /* Ack masked but set interrupts */ + reg = chip->status_base + + (i * map->reg_stride * d->irq_reg_stride); + ret = regmap_read(map, reg, &d->status_buf[i]); + if (ret != 0) { + dev_err(map->dev, "Failed to read IRQ status: %d\n", + ret); + goto err_alloc; + } + + if (d->status_buf[i] && chip->ack_base) { + reg = chip->ack_base + + (i * map->reg_stride * d->irq_reg_stride); + ret = regmap_write(map, reg, + d->status_buf[i] & d->mask_buf[i]); + if (ret != 0) { + dev_err(map->dev, "Failed to ack 0x%x: %d\n", + reg, ret); + goto err_alloc; + } + } } /* Wake is disabled by default */ diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 75981d0b57dc..34ebe7778033 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -470,6 +470,7 @@ struct regmap_irq { * @ack_base: Base ack address. If zero then the chip is clear on read. * @wake_base: Base address for wake enables. If zero unsupported. * @irq_reg_stride: Stride to use for chips where registers are not contiguous. + * @init_ack_masked: Ack all masked interrupts once during initalization. * @runtime_pm: Hold a runtime PM lock on the device when accessing it. * * @num_regs: Number of registers in each control bank. @@ -485,6 +486,7 @@ struct regmap_irq_chip { unsigned int ack_base; unsigned int wake_base; unsigned int irq_reg_stride; + bool init_ack_masked; unsigned int mask_invert; unsigned int wake_invert; bool runtime_pm; -- cgit v1.2.3-71-gd317 From 36697529b5bbe36911e39a6309e7a7c9250d280a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 07:04:48 -0400 Subject: tty: Replace ldisc locking with ldisc_sem Line discipline locking was performed with a combination of a mutex, a status bit, a count, and a waitqueue -- basically, a rw semaphore. Replace the existing combination with an ld_semaphore. Fixes: 1) the 'reference acquire after ldisc locked' bug 2) the over-complicated halt mechanism 3) lock order wrt. tty_lock() 4) dropping locks while changing ldisc 5) previously unidentified deadlock while locking ldisc from both linked ttys concurrently 6) previously unidentified recursive deadlocks Adds much-needed lockdep diagnostics. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 2 +- drivers/tty/tty_io.c | 7 +- drivers/tty/tty_ldisc.c | 329 +++++++--------------------------------------- include/linux/tty.h | 4 +- include/linux/tty_ldisc.h | 3 +- 5 files changed, 52 insertions(+), 293 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 9121c1f7aeef..a42a028a9d4e 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -429,7 +429,7 @@ static void flush_to_ldisc(struct work_struct *work) return; disc = tty_ldisc_ref(tty); - if (disc == NULL) /* !TTY_LDISC */ + if (disc == NULL) return; spin_lock_irqsave(&buf->lock, flags); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index fa80af3656a3..0fa5db4c7a7a 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1388,8 +1388,7 @@ static int tty_reopen(struct tty_struct *tty) struct tty_driver *driver = tty->driver; if (test_bit(TTY_CLOSING, &tty->flags) || - test_bit(TTY_HUPPING, &tty->flags) || - test_bit(TTY_LDISC_CHANGING, &tty->flags)) + test_bit(TTY_HUPPING, &tty->flags)) return -EIO; if (driver->type == TTY_DRIVER_TYPE_PTY && @@ -1405,7 +1404,7 @@ static int tty_reopen(struct tty_struct *tty) } tty->count++; - WARN_ON(!test_bit(TTY_LDISC, &tty->flags)); + WARN_ON(!tty->ldisc); return 0; } @@ -3017,7 +3016,7 @@ void initialize_tty_struct(struct tty_struct *tty, tty->pgrp = NULL; mutex_init(&tty->legacy_mutex); mutex_init(&tty->termios_mutex); - mutex_init(&tty->ldisc_mutex); + init_ldsem(&tty->ldisc_sem); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); INIT_WORK(&tty->hangup_work, do_tty_hangup); diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 418c9f64a9fd..b7b8048f1253 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -45,7 +45,6 @@ enum { */ static DEFINE_RAW_SPINLOCK(tty_ldiscs_lock); -static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); /* Line disc dispatch table */ static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; @@ -153,7 +152,7 @@ static void put_ldops(struct tty_ldisc_ops *ldops) * takes tty_ldiscs_lock to guard against ldisc races */ -static struct tty_ldisc *tty_ldisc_get(int disc) +static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc) { struct tty_ldisc *ld; struct tty_ldisc_ops *ldops; @@ -180,8 +179,7 @@ static struct tty_ldisc *tty_ldisc_get(int disc) } ld->ops = ldops; - atomic_set(&ld->users, 1); - init_waitqueue_head(&ld->wq_idle); + ld->tty = tty; return ld; } @@ -193,20 +191,11 @@ static struct tty_ldisc *tty_ldisc_get(int disc) */ static inline void tty_ldisc_put(struct tty_ldisc *ld) { - unsigned long flags; - if (WARN_ON_ONCE(!ld)) return; - raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); - - /* unreleased reader reference(s) will cause this WARN */ - WARN_ON(!atomic_dec_and_test(&ld->users)); - - ld->ops->refcount--; - module_put(ld->ops->owner); + put_ldops(ld->ops); kfree(ld); - raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); } static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) @@ -257,34 +246,6 @@ const struct file_operations tty_ldiscs_proc_fops = { .release = seq_release, }; -/** - * tty_ldisc_try - internal helper - * @tty: the tty - * - * Make a single attempt to grab and bump the refcount on - * the tty ldisc. Return 0 on failure or 1 on success. This is - * used to implement both the waiting and non waiting versions - * of tty_ldisc_ref - * - * Locking: takes tty_ldiscs_lock - */ - -static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty) -{ - unsigned long flags; - struct tty_ldisc *ld; - - /* FIXME: this allows reference acquire after TTY_LDISC is cleared */ - raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); - ld = NULL; - if (test_bit(TTY_LDISC, &tty->flags) && tty->ldisc) { - ld = tty->ldisc; - atomic_inc(&ld->users); - } - raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); - return ld; -} - /** * tty_ldisc_ref_wait - wait for the tty ldisc * @tty: tty device @@ -298,16 +259,15 @@ static struct tty_ldisc *tty_ldisc_try(struct tty_struct *tty) * against a discipline change, such as an existing ldisc reference * (which we check for) * - * Locking: call functions take tty_ldiscs_lock + * Note: only callable from a file_operations routine (which + * guarantees tty->ldisc != NULL when the lock is acquired). */ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) { - struct tty_ldisc *ld; - - /* wait_event is a macro */ - wait_event(tty_ldisc_wait, (ld = tty_ldisc_try(tty)) != NULL); - return ld; + ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT); + WARN_ON(!tty->ldisc); + return tty->ldisc; } EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); @@ -318,13 +278,18 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); * Dereference the line discipline for the terminal and take a * reference to it. If the line discipline is in flux then * return NULL. Can be called from IRQ and timer functions. - * - * Locking: called functions take tty_ldiscs_lock */ struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) { - return tty_ldisc_try(tty); + struct tty_ldisc *ld = NULL; + + if (ldsem_down_read_trylock(&tty->ldisc_sem)) { + ld = tty->ldisc; + if (!ld) + ldsem_up_read(&tty->ldisc_sem); + } + return ld; } EXPORT_SYMBOL_GPL(tty_ldisc_ref); @@ -334,27 +299,11 @@ EXPORT_SYMBOL_GPL(tty_ldisc_ref); * * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May * be called in IRQ context. - * - * Locking: takes tty_ldiscs_lock */ void tty_ldisc_deref(struct tty_ldisc *ld) { - unsigned long flags; - - if (WARN_ON_ONCE(!ld)) - return; - - raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); - /* - * WARNs if one-too-many reader references were released - * - the last reference must be released with tty_ldisc_put - */ - WARN_ON(atomic_dec_and_test(&ld->users)); - raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); - - if (waitqueue_active(&ld->wq_idle)) - wake_up(&ld->wq_idle); + ldsem_up_read(&ld->tty->ldisc_sem); } EXPORT_SYMBOL_GPL(tty_ldisc_deref); @@ -437,27 +386,6 @@ static void __lockfunc tty_ldisc_enable_pair(struct tty_struct *tty, tty_ldisc_unlock_pair(tty, tty2); } - -/** - * tty_ldisc_enable - allow ldisc use - * @tty: terminal to activate ldisc on - * - * Set the TTY_LDISC flag when the line discipline can be called - * again. Do necessary wakeups for existing sleepers. Clear the LDISC - * changing flag to indicate any ldisc change is now over. - * - * Note: nobody should set the TTY_LDISC bit except via this function. - * Clearing directly is allowed. - */ - -static void tty_ldisc_enable(struct tty_struct *tty) -{ - clear_bit(TTY_LDISC_HALTED, &tty->flags); - set_bit(TTY_LDISC, &tty->flags); - clear_bit(TTY_LDISC_CHANGING, &tty->flags); - wake_up(&tty_ldisc_wait); -} - /** * tty_ldisc_flush - flush line discipline queue * @tty: tty @@ -555,14 +483,14 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) int r; /* There is an outstanding reference here so this is safe */ - old = tty_ldisc_get(old->ops->num); + old = tty_ldisc_get(tty, old->ops->num); WARN_ON(IS_ERR(old)); tty->ldisc = old; tty_set_termios_ldisc(tty, old->ops->num); if (tty_ldisc_open(tty, old) < 0) { tty_ldisc_put(old); /* This driver is always present */ - new_ldisc = tty_ldisc_get(N_TTY); + new_ldisc = tty_ldisc_get(tty, N_TTY); if (IS_ERR(new_ldisc)) panic("n_tty: get"); tty->ldisc = new_ldisc; @@ -575,101 +503,6 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) } } -/** - * tty_ldisc_wait_idle - wait for the ldisc to become idle - * @tty: tty to wait for - * @timeout: for how long to wait at most - * - * Wait for the line discipline to become idle. The discipline must - * have been halted for this to guarantee it remains idle. - */ -static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) -{ - long ret; - ret = wait_event_timeout(tty->ldisc->wq_idle, - atomic_read(&tty->ldisc->users) == 1, timeout); - return ret > 0 ? 0 : -EBUSY; -} - -/** - * tty_ldisc_halt - shut down the line discipline - * @tty: tty device - * @o_tty: paired pty device (can be NULL) - * @timeout: # of jiffies to wait for ldisc refs to be released - * - * Shut down the line discipline and work queue for this tty device and - * its paired pty (if exists). Clearing the TTY_LDISC flag ensures - * no further references can be obtained, while waiting for existing - * references to be released ensures no more data is fed to the ldisc. - * - * You need to do a 'flush_scheduled_work()' (outside the ldisc_mutex) - * in order to make sure any currently executing ldisc work is also - * flushed. - */ - -static int tty_ldisc_halt(struct tty_struct *tty, struct tty_struct *o_tty, - long timeout) -{ - int retval; - - clear_bit(TTY_LDISC, &tty->flags); - if (o_tty) - clear_bit(TTY_LDISC, &o_tty->flags); - - retval = tty_ldisc_wait_idle(tty, timeout); - if (!retval && o_tty) - retval = tty_ldisc_wait_idle(o_tty, timeout); - if (retval) - return retval; - - set_bit(TTY_LDISC_HALTED, &tty->flags); - if (o_tty) - set_bit(TTY_LDISC_HALTED, &o_tty->flags); - - return 0; -} - -/** - * tty_ldisc_hangup_halt - halt the line discipline for hangup - * @tty: tty being hung up - * - * Shut down the line discipline and work queue for the tty device - * being hungup. Clear the TTY_LDISC flag to ensure no further - * references can be obtained and wait for remaining references to be - * released to ensure no more data is fed to this ldisc. - * Caller must hold legacy and ->ldisc_mutex. - * - * NB: tty_set_ldisc() is prevented from changing the ldisc concurrently - * with this function by checking the TTY_HUPPING flag. - */ -static bool tty_ldisc_hangup_halt(struct tty_struct *tty) -{ - char cur_n[TASK_COMM_LEN], tty_n[64]; - long timeout = 3 * HZ; - - clear_bit(TTY_LDISC, &tty->flags); - - if (tty->ldisc) { /* Not yet closed */ - tty_unlock(tty); - - while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) { - timeout = MAX_SCHEDULE_TIMEOUT; - printk_ratelimited(KERN_WARNING - "%s: waiting (%s) for %s took too long, but we keep waiting...\n", - __func__, get_task_comm(cur_n, current), - tty_name(tty, tty_n)); - } - - set_bit(TTY_LDISC_HALTED, &tty->flags); - - /* must reacquire both locks and preserve lock order */ - mutex_unlock(&tty->ldisc_mutex); - tty_lock(tty); - mutex_lock(&tty->ldisc_mutex); - } - return !!tty->ldisc; -} - /** * tty_set_ldisc - set line discipline * @tty: the terminal to set @@ -679,103 +512,47 @@ static bool tty_ldisc_hangup_halt(struct tty_struct *tty) * context. The ldisc change logic has to protect itself against any * overlapping ldisc change (including on the other end of pty pairs), * the close of one side of a tty/pty pair, and eventually hangup. - * - * Locking: takes tty_ldiscs_lock, termios_mutex */ int tty_set_ldisc(struct tty_struct *tty, int ldisc) { int retval; struct tty_ldisc *o_ldisc, *new_ldisc; - struct tty_struct *o_tty; + struct tty_struct *o_tty = tty->link; - new_ldisc = tty_ldisc_get(ldisc); + new_ldisc = tty_ldisc_get(tty, ldisc); if (IS_ERR(new_ldisc)) return PTR_ERR(new_ldisc); - tty_lock(tty); - /* - * We need to look at the tty locking here for pty/tty pairs - * when both sides try to change in parallel. - */ - - o_tty = tty->link; /* o_tty is the pty side or NULL */ - + retval = tty_ldisc_lock_pair_timeout(tty, o_tty, 5 * HZ); + if (retval) { + tty_ldisc_put(new_ldisc); + return retval; + } /* * Check the no-op case */ if (tty->ldisc->ops->num == ldisc) { - tty_unlock(tty); + tty_ldisc_enable_pair(tty, o_tty); tty_ldisc_put(new_ldisc); return 0; } - mutex_lock(&tty->ldisc_mutex); - - /* - * We could be midstream of another ldisc change which has - * dropped the lock during processing. If so we need to wait. - */ - - while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) { - mutex_unlock(&tty->ldisc_mutex); - tty_unlock(tty); - wait_event(tty_ldisc_wait, - test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); - tty_lock(tty); - mutex_lock(&tty->ldisc_mutex); - } - - set_bit(TTY_LDISC_CHANGING, &tty->flags); - - /* - * No more input please, we are switching. The new ldisc - * will update this value in the ldisc open function - */ - + /* FIXME: why 'shutoff' input if the ldisc is locked? */ tty->receive_room = 0; o_ldisc = tty->ldisc; - - tty_unlock(tty); - /* - * Make sure we don't change while someone holds a - * reference to the line discipline. The TTY_LDISC bit - * prevents anyone taking a reference once it is clear. - * We need the lock to avoid racing reference takers. - * - * We must clear the TTY_LDISC bit here to avoid a livelock - * with a userspace app continually trying to use the tty in - * parallel to the change and re-referencing the tty. - */ - - retval = tty_ldisc_halt(tty, o_tty, 5 * HZ); - - /* - * Wait for hangup to complete, if pending. - * We must drop the mutex here in case a hangup is also in process. - */ - - mutex_unlock(&tty->ldisc_mutex); - - flush_work(&tty->hangup_work); - tty_lock(tty); - mutex_lock(&tty->ldisc_mutex); - /* handle wait idle failure locked */ - if (retval) { - tty_ldisc_put(new_ldisc); - goto enable; - } + /* FIXME: for testing only */ + WARN_ON(test_bit(TTY_HUPPED, &tty->flags)); if (test_bit(TTY_HUPPING, &tty->flags)) { /* We were raced by the hangup method. It will have stomped the ldisc data and closed the ldisc down */ - clear_bit(TTY_LDISC_CHANGING, &tty->flags); - mutex_unlock(&tty->ldisc_mutex); + tty_ldisc_enable_pair(tty, o_tty); tty_ldisc_put(new_ldisc); tty_unlock(tty); return -EIO; @@ -804,14 +581,10 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) tty_ldisc_put(o_ldisc); -enable: /* * Allow ldisc referencing to occur again */ - - tty_ldisc_enable(tty); - if (o_tty) - tty_ldisc_enable(o_tty); + tty_ldisc_enable_pair(tty, o_tty); /* Restart the work queue in case no characters kick it off. Safe if already running */ @@ -819,7 +592,6 @@ enable: if (o_tty) schedule_work(&o_tty->port->buf.work); - mutex_unlock(&tty->ldisc_mutex); tty_unlock(tty); return retval; } @@ -852,7 +624,7 @@ static void tty_reset_termios(struct tty_struct *tty) static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc) { - struct tty_ldisc *ld = tty_ldisc_get(ldisc); + struct tty_ldisc *ld = tty_ldisc_get(tty, ldisc); if (IS_ERR(ld)) return -1; @@ -891,14 +663,8 @@ void tty_ldisc_hangup(struct tty_struct *tty) tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc); - /* - * FIXME! What are the locking issues here? This may me overdoing - * things... This question is especially important now that we've - * removed the irqlock. - */ ld = tty_ldisc_ref(tty); if (ld != NULL) { - /* We may have no line discipline at this point */ if (ld->ops->flush_buffer) ld->ops->flush_buffer(tty); tty_driver_flush_buffer(tty); @@ -909,21 +675,22 @@ void tty_ldisc_hangup(struct tty_struct *tty) ld->ops->hangup(tty); tty_ldisc_deref(ld); } - /* - * FIXME: Once we trust the LDISC code better we can wait here for - * ldisc completion and fix the driver call race - */ + wake_up_interruptible_poll(&tty->write_wait, POLLOUT); wake_up_interruptible_poll(&tty->read_wait, POLLIN); + + tty_unlock(tty); + /* * Shutdown the current line discipline, and reset it to * N_TTY if need be. * * Avoid racing set_ldisc or tty_ldisc_release */ - mutex_lock(&tty->ldisc_mutex); + tty_ldisc_lock_pair(tty, tty->link); + tty_lock(tty); - if (tty_ldisc_hangup_halt(tty)) { + if (tty->ldisc) { /* At this point we have a halted ldisc; we want to close it and reopen a new ldisc. We could defer the reopen to the next @@ -942,9 +709,8 @@ void tty_ldisc_hangup(struct tty_struct *tty) BUG_ON(tty_ldisc_reinit(tty, N_TTY)); WARN_ON(tty_ldisc_open(tty, tty->ldisc)); } - tty_ldisc_enable(tty); } - mutex_unlock(&tty->ldisc_mutex); + tty_ldisc_enable_pair(tty, tty->link); if (reset) tty_reset_termios(tty); @@ -976,15 +742,12 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) tty_ldisc_close(tty, ld); return retval; } - tty_ldisc_enable(o_tty); } - tty_ldisc_enable(tty); return 0; } static void tty_ldisc_kill(struct tty_struct *tty) { - mutex_lock(&tty->ldisc_mutex); /* * Now kill off the ldisc */ @@ -995,7 +758,6 @@ static void tty_ldisc_kill(struct tty_struct *tty) /* Ensure the next open requests the N_TTY ldisc */ tty_set_termios_ldisc(tty, N_TTY); - mutex_unlock(&tty->ldisc_mutex); } /** @@ -1017,15 +779,16 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc); - tty_ldisc_halt(tty, o_tty, MAX_SCHEDULE_TIMEOUT); - + tty_ldisc_lock_pair(tty, o_tty); tty_lock_pair(tty, o_tty); - /* This will need doing differently if we need to lock */ + tty_ldisc_kill(tty); if (o_tty) tty_ldisc_kill(o_tty); tty_unlock_pair(tty, o_tty); + tty_ldisc_unlock_pair(tty, o_tty); + /* And the memory resources remaining (buffers, termios) will be disposed of when the kref hits zero */ @@ -1042,7 +805,7 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) void tty_ldisc_init(struct tty_struct *tty) { - struct tty_ldisc *ld = tty_ldisc_get(N_TTY); + struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY); if (IS_ERR(ld)) panic("n_tty: init_tty"); tty->ldisc = ld; diff --git a/include/linux/tty.h b/include/linux/tty.h index 01ac30efd6a6..7269daf7632b 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -238,7 +238,7 @@ struct tty_struct { int index; /* Protects ldisc changes: Lock tty not pty */ - struct mutex ldisc_mutex; + struct ld_semaphore ldisc_sem; struct tty_ldisc *ldisc; struct mutex atomic_write_lock; @@ -305,8 +305,6 @@ struct tty_file_private { #define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */ #define TTY_PUSH 6 /* n_tty private */ #define TTY_CLOSING 7 /* ->close() in progress */ -#define TTY_LDISC 9 /* Line discipline attached */ -#define TTY_LDISC_CHANGING 10 /* Line discipline changing */ #define TTY_LDISC_OPEN 11 /* Line discipline is open */ #define TTY_PTY_LOCK 16 /* pty private */ #define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index a1b048999821..23bdd9debb84 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -203,8 +203,7 @@ struct tty_ldisc_ops { struct tty_ldisc { struct tty_ldisc_ops *ops; - atomic_t users; - wait_queue_head_t wq_idle; + struct tty_struct *tty; }; #define TTY_LDISC_MAGIC 0x5403 -- cgit v1.2.3-71-gd317 From 24a89d1cb69b6c488cf16d98dd02e7820f62b40c Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:14:15 -0400 Subject: tty: Make ldisc input flow control concurrency-friendly Although line discipline receiving is single-producer/single-consumer, using tty->receive_room to manage flow control creates unnecessary critical regions requiring additional lock use. Instead, introduce the optional .receive_buf2() ldisc method which returns the # of bytes actually received. Serialization is guaranteed by the caller. In turn, the line discipline should schedule the buffer work item whenever space becomes available; ie., when there is room to receive data and receive_room() previously returned 0 (the buffer work item stops processing if receive_buf2() returns 0). Note the 'no room' state need not be atomic despite concurrent use by two threads because only the buffer work thread can set the state and only the read() thread can clear the state. Add n_tty_receive_buf2() as the receive_buf2() method for N_TTY. Provide a public helper function, tty_ldisc_receive_buf(), to use when directly accessing the receive_buf() methods. Line disciplines not using input flow control can continue to set tty->receive_room to a fixed value and only provide the receive_buf() method. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 72 +++++++++++++++++++++++++++++----------------- drivers/tty/tty_buffer.c | 13 ++++++--- drivers/tty/vt/selection.c | 4 +-- include/linux/tty.h | 13 +++++++++ include/linux/tty_ldisc.h | 13 +++++++++ 5 files changed, 82 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 4bf0fc0843d7..eddeb7889e62 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -79,6 +79,9 @@ struct n_tty_data { unsigned long overrun_time; int num_overrun; + /* non-atomic */ + bool no_room; + unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; unsigned char echo_overrun:1; @@ -114,25 +117,10 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, return put_user(x, ptr); } -/** - * n_tty_set_room - receive space - * @tty: terminal - * - * Updates tty->receive_room to reflect the currently available space - * in the input buffer, and re-schedules the flip buffer work if space - * just became available. - * - * Locks: Concurrent update is protected with read_lock - */ - -static int set_room(struct tty_struct *tty) +static int receive_room(struct tty_struct *tty) { struct n_tty_data *ldata = tty->disc_data; int left; - int old_left; - unsigned long flags; - - raw_spin_lock_irqsave(&ldata->read_lock, flags); if (I_PARMRK(tty)) { /* Multiply read_cnt by 3, since each byte might take up to @@ -150,18 +138,27 @@ static int set_room(struct tty_struct *tty) */ if (left <= 0) left = ldata->icanon && !ldata->canon_data; - old_left = tty->receive_room; - tty->receive_room = left; - raw_spin_unlock_irqrestore(&ldata->read_lock, flags); - - return left && !old_left; + return left; } +/** + * n_tty_set_room - receive space + * @tty: terminal + * + * Re-schedules the flip buffer work if space just became available. + * + * Locks: Concurrent update is protected with read_lock + */ + static void n_tty_set_room(struct tty_struct *tty) { + struct n_tty_data *ldata = tty->disc_data; + /* Did this open up the receive buffer? We may need to flip */ - if (set_room(tty)) { + if (unlikely(ldata->no_room) && receive_room(tty)) { + ldata->no_room = 0; + WARN_RATELIMIT(tty->port->itty == NULL, "scheduling with invalid itty\n"); /* see if ldisc has been killed - if so, this means that @@ -1408,8 +1405,8 @@ static void n_tty_write_wakeup(struct tty_struct *tty) * calls one at a time and in order (or using flush_to_ldisc) */ -static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, - char *fp, int count) +static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) { struct n_tty_data *ldata = tty->disc_data; const unsigned char *p; @@ -1464,8 +1461,6 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, tty->ops->flush_chars(tty); } - set_room(tty); - if ((!ldata->icanon && (ldata->read_cnt >= ldata->minimum_to_wake)) || L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); @@ -1480,7 +1475,7 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, */ while (1) { tty_set_flow_change(tty, TTY_THROTTLE_SAFE); - if (tty->receive_room >= TTY_THRESHOLD_THROTTLE) + if (receive_room(tty) >= TTY_THRESHOLD_THROTTLE) break; if (!tty_throttle_safe(tty)) break; @@ -1488,6 +1483,28 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, __tty_set_flow_change(tty, 0); } +static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + __receive_buf(tty, cp, fp, count); +} + +static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + int room; + + tty->receive_room = room = receive_room(tty); + if (!room) + ldata->no_room = 1; + count = min(count, room); + if (count) + __receive_buf(tty, cp, fp, count); + + return count; +} + int is_ignored(int sig) { return (sigismember(¤t->blocked, sig) || @@ -2203,6 +2220,7 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = { .receive_buf = n_tty_receive_buf, .write_wakeup = n_tty_write_wakeup, .fasync = n_tty_fasync, + .receive_buf2 = n_tty_receive_buf2, }; /** diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 6c7a1d043c76..ff1b2e37c3ca 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -407,11 +407,16 @@ static int receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) { struct tty_ldisc *disc = tty->ldisc; + char *p = head->char_buf_ptr + head->read; + unsigned char *f = head->flag_buf_ptr + head->read; - count = min_t(int, count, tty->receive_room); - if (count) - disc->ops->receive_buf(tty, head->char_buf_ptr + head->read, - head->flag_buf_ptr + head->read, count); + if (disc->ops->receive_buf2) + count = disc->ops->receive_buf2(tty, p, f, count); + else { + count = min_t(int, count, tty->receive_room); + if (count) + disc->ops->receive_buf(tty, p, f, count); + } head->read += count; return count; } diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 60b7b6926059..2ca8d6b6514c 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -356,8 +356,8 @@ int paste_selection(struct tty_struct *tty) continue; } count = sel_buffer_lth - pasted; - count = min(count, tty->receive_room); - ld->ops->receive_buf(tty, sel_buffer + pasted, NULL, count); + count = tty_ldisc_receive_buf(ld, sel_buffer + pasted, NULL, + count); pasted += count; } remove_wait_queue(&vc->paste_wait, &wait); diff --git a/include/linux/tty.h b/include/linux/tty.h index 7269daf7632b..8323ee4f95b9 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -557,6 +557,19 @@ extern void tty_ldisc_init(struct tty_struct *tty); extern void tty_ldisc_deinit(struct tty_struct *tty); extern void tty_ldisc_begin(void); +static inline int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p, + char *f, int count) +{ + if (ld->ops->receive_buf2) + count = ld->ops->receive_buf2(ld->tty, p, f, count); + else { + count = min_t(int, count, ld->tty->receive_room); + if (count) + ld->ops->receive_buf(ld->tty, p, f, count); + } + return count; +} + /* n_tty.c */ extern struct tty_ldisc_ops tty_ldisc_N_TTY; diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 23bdd9debb84..f15c898ff462 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -109,6 +109,17 @@ * * Tells the discipline that the DCD pin has changed its status. * Used exclusively by the N_PPS (Pulse-Per-Second) line discipline. + * + * int (*receive_buf2)(struct tty_struct *, const unsigned char *cp, + * char *fp, int count); + * + * This function is called by the low-level tty driver to send + * characters received by the hardware to the line discpline for + * processing. is a pointer to the buffer of input + * character received by the device. is a pointer to a + * pointer of flag bytes which indicate whether a character was + * received with a parity error, etc. + * If assigned, prefer this function for automatic flow control. */ #include @@ -195,6 +206,8 @@ struct tty_ldisc_ops { void (*write_wakeup)(struct tty_struct *); void (*dcd_change)(struct tty_struct *, unsigned int); void (*fasync)(struct tty_struct *tty, int on); + int (*receive_buf2)(struct tty_struct *, const unsigned char *cp, + char *fp, int count); struct module *owner; -- cgit v1.2.3-71-gd317 From 6a1c0680cf3ba94356ecd58833e1540c93472a57 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:14:23 -0400 Subject: tty: Convert termios_mutex to termios_rwsem termios is commonly accessed unsafely (especially by N_TTY) because the existing mutex forces exclusive access. Convert existing usage. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/net/irda/irtty-sir.c | 8 ++-- drivers/tty/n_tty.c | 2 +- drivers/tty/pty.c | 4 +- drivers/tty/tty_io.c | 14 +++---- drivers/tty/tty_ioctl.c | 90 ++++++++++++++++++++++---------------------- drivers/tty/tty_ldisc.c | 10 ++--- drivers/tty/vt/vt.c | 4 +- include/linux/tty.h | 7 ++-- 8 files changed, 70 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index a41267197839..177441afeb96 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -123,14 +123,14 @@ static int irtty_change_speed(struct sir_dev *dev, unsigned speed) tty = priv->tty; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); old_termios = tty->termios; cflag = tty->termios.c_cflag; tty_encode_baud_rate(tty, speed, speed); if (tty->ops->set_termios) tty->ops->set_termios(tty, &old_termios); priv->io.speed = speed; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } @@ -280,7 +280,7 @@ static inline void irtty_stop_receiver(struct tty_struct *tty, int stop) struct ktermios old_termios; int cflag; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); old_termios = tty->termios; cflag = tty->termios.c_cflag; @@ -292,7 +292,7 @@ static inline void irtty_stop_receiver(struct tty_struct *tty, int stop) tty->termios.c_cflag = cflag; if (tty->ops->set_termios) tty->ops->set_termios(tty, &old_termios); - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); } /*****************************************************************/ diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index d159059560e8..ab923bb2b262 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1539,7 +1539,7 @@ int is_ignored(int sig) * guaranteed that this function will not be re-entered or in progress * when the ldisc is closed. * - * Locking: Caller holds tty->termios_mutex + * Locking: Caller holds tty->termios_rwsem */ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index abfd99089781..1b39dd639ee9 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -287,7 +287,7 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws) struct tty_struct *pty = tty->link; /* For a PTY we need to lock the tty side */ - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); if (!memcmp(ws, &tty->winsize, sizeof(*ws))) goto done; @@ -314,7 +314,7 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws) tty->winsize = *ws; pty->winsize = *ws; /* Never used so will go away soon */ done: - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 0fa5db4c7a7a..639e24ade9bf 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -604,7 +604,7 @@ static int tty_signal_session_leader(struct tty_struct *tty, int exit_session) * redirect lock for undoing redirection * file list lock for manipulating list of ttys * tty_ldiscs_lock from called functions - * termios_mutex resetting termios data + * termios_rwsem resetting termios data * tasklist_lock to walk task list for hangup event * ->siglock to protect ->signal/->sighand */ @@ -2230,7 +2230,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p) * * Copies the kernel idea of the window size into the user buffer. * - * Locking: tty->termios_mutex is taken to ensure the winsize data + * Locking: tty->termios_rwsem is taken to ensure the winsize data * is consistent. */ @@ -2238,9 +2238,9 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg) { int err; - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); err = copy_to_user(arg, &tty->winsize, sizeof(*arg)); - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); return err ? -EFAULT: 0; } @@ -2261,7 +2261,7 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws) unsigned long flags; /* Lock the tty */ - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); if (!memcmp(ws, &tty->winsize, sizeof(*ws))) goto done; /* Get the PID values and reference them so we can @@ -2276,7 +2276,7 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws) tty->winsize = *ws; done: - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } EXPORT_SYMBOL(tty_do_resize); @@ -3015,7 +3015,7 @@ void initialize_tty_struct(struct tty_struct *tty, tty->session = NULL; tty->pgrp = NULL; mutex_init(&tty->legacy_mutex); - mutex_init(&tty->termios_mutex); + init_rwsem(&tty->termios_rwsem); init_ldsem(&tty->ldisc_sem); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 3500d4114147..9ce20df8a2c8 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -94,20 +94,20 @@ EXPORT_SYMBOL(tty_driver_flush_buffer); * @tty: terminal * * Indicate that a tty should stop transmitting data down the stack. - * Takes the termios mutex to protect against parallel throttle/unthrottle + * Takes the termios rwsem to protect against parallel throttle/unthrottle * and also to ensure the driver can consistently reference its own * termios data at this point when implementing software flow control. */ void tty_throttle(struct tty_struct *tty) { - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); /* check TTY_THROTTLED first so it indicates our state */ if (!test_and_set_bit(TTY_THROTTLED, &tty->flags) && tty->ops->throttle) tty->ops->throttle(tty); tty->flow_change = 0; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); } EXPORT_SYMBOL(tty_throttle); @@ -116,7 +116,7 @@ EXPORT_SYMBOL(tty_throttle); * @tty: terminal * * Indicate that a tty may continue transmitting data down the stack. - * Takes the termios mutex to protect against parallel throttle/unthrottle + * Takes the termios rwsem to protect against parallel throttle/unthrottle * and also to ensure the driver can consistently reference its own * termios data at this point when implementing software flow control. * @@ -126,12 +126,12 @@ EXPORT_SYMBOL(tty_throttle); void tty_unthrottle(struct tty_struct *tty) { - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && tty->ops->unthrottle) tty->ops->unthrottle(tty); tty->flow_change = 0; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); } EXPORT_SYMBOL(tty_unthrottle); @@ -151,7 +151,7 @@ int tty_throttle_safe(struct tty_struct *tty) { int ret = 0; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); if (!test_bit(TTY_THROTTLED, &tty->flags)) { if (tty->flow_change != TTY_THROTTLE_SAFE) ret = 1; @@ -161,7 +161,7 @@ int tty_throttle_safe(struct tty_struct *tty) tty->ops->throttle(tty); } } - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return ret; } @@ -182,7 +182,7 @@ int tty_unthrottle_safe(struct tty_struct *tty) { int ret = 0; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); if (test_bit(TTY_THROTTLED, &tty->flags)) { if (tty->flow_change != TTY_UNTHROTTLE_SAFE) ret = 1; @@ -192,7 +192,7 @@ int tty_unthrottle_safe(struct tty_struct *tty) tty->ops->unthrottle(tty); } } - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return ret; } @@ -468,7 +468,7 @@ EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate); * @obad: output baud rate * * Update the current termios data for the tty with the new speed - * settings. The caller must hold the termios_mutex for the tty in + * settings. The caller must hold the termios_rwsem for the tty in * question. */ @@ -528,7 +528,7 @@ EXPORT_SYMBOL(tty_termios_hw_change); * is a bit of layering violation here with n_tty in terms of the * internal knowledge of this function. * - * Locking: termios_mutex + * Locking: termios_rwsem */ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) @@ -544,7 +544,7 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) /* FIXME: we need to decide on some locking/ordering semantics for the set_termios notification eventually */ - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); old_termios = tty->termios; tty->termios = *new_termios; unset_locked_termios(&tty->termios, &old_termios, &tty->termios_locked); @@ -586,7 +586,7 @@ int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios) (ld->ops->set_termios)(tty, &old_termios); tty_ldisc_deref(ld); } - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } EXPORT_SYMBOL_GPL(tty_set_termios); @@ -601,7 +601,7 @@ EXPORT_SYMBOL_GPL(tty_set_termios); * functions before using tty_set_termios to do the actual changes. * * Locking: - * Called functions take ldisc and termios_mutex locks + * Called functions take ldisc and termios_rwsem locks */ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) @@ -613,9 +613,9 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) if (retval) return retval; - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); tmp_termios = tty->termios; - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); if (opt & TERMIOS_TERMIO) { if (user_termio_to_kernel_termios(&tmp_termios, @@ -667,16 +667,16 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) static void copy_termios(struct tty_struct *tty, struct ktermios *kterm) { - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); *kterm = tty->termios; - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); } static void copy_termios_locked(struct tty_struct *tty, struct ktermios *kterm) { - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); *kterm = tty->termios_locked; - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); } static int get_termio(struct tty_struct *tty, struct termio __user *termio) @@ -723,10 +723,10 @@ static int set_termiox(struct tty_struct *tty, void __user *arg, int opt) return -ERESTARTSYS; } - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); if (tty->ops->set_termiox) tty->ops->set_termiox(tty, &tnew); - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } @@ -761,13 +761,13 @@ static int get_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) { struct sgttyb tmp; - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); tmp.sg_ispeed = tty->termios.c_ispeed; tmp.sg_ospeed = tty->termios.c_ospeed; tmp.sg_erase = tty->termios.c_cc[VERASE]; tmp.sg_kill = tty->termios.c_cc[VKILL]; tmp.sg_flags = get_sgflags(tty); - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -806,7 +806,7 @@ static void set_sgflags(struct ktermios *termios, int flags) * Updates a terminal from the legacy BSD style terminal information * structure. * - * Locking: termios_mutex + * Locking: termios_rwsem */ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) @@ -822,7 +822,7 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) if (copy_from_user(&tmp, sgttyb, sizeof(tmp))) return -EFAULT; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); termios = tty->termios; termios.c_cc[VERASE] = tmp.sg_erase; termios.c_cc[VKILL] = tmp.sg_kill; @@ -832,7 +832,7 @@ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) tty_termios_encode_baud_rate(&termios, termios.c_ispeed, termios.c_ospeed); #endif - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); tty_set_termios(tty, &termios); return 0; } @@ -843,14 +843,14 @@ static int get_tchars(struct tty_struct *tty, struct tchars __user *tchars) { struct tchars tmp; - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); tmp.t_intrc = tty->termios.c_cc[VINTR]; tmp.t_quitc = tty->termios.c_cc[VQUIT]; tmp.t_startc = tty->termios.c_cc[VSTART]; tmp.t_stopc = tty->termios.c_cc[VSTOP]; tmp.t_eofc = tty->termios.c_cc[VEOF]; tmp.t_brkc = tty->termios.c_cc[VEOL2]; /* what is brkc anyway? */ - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); return copy_to_user(tchars, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -860,14 +860,14 @@ static int set_tchars(struct tty_struct *tty, struct tchars __user *tchars) if (copy_from_user(&tmp, tchars, sizeof(tmp))) return -EFAULT; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); tty->termios.c_cc[VINTR] = tmp.t_intrc; tty->termios.c_cc[VQUIT] = tmp.t_quitc; tty->termios.c_cc[VSTART] = tmp.t_startc; tty->termios.c_cc[VSTOP] = tmp.t_stopc; tty->termios.c_cc[VEOF] = tmp.t_eofc; tty->termios.c_cc[VEOL2] = tmp.t_brkc; /* what is brkc anyway? */ - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } #endif @@ -877,7 +877,7 @@ static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) { struct ltchars tmp; - mutex_lock(&tty->termios_mutex); + down_read(&tty->termios_rwsem); tmp.t_suspc = tty->termios.c_cc[VSUSP]; /* what is dsuspc anyway? */ tmp.t_dsuspc = tty->termios.c_cc[VSUSP]; @@ -886,7 +886,7 @@ static int get_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) tmp.t_flushc = tty->termios.c_cc[VEOL2]; tmp.t_werasc = tty->termios.c_cc[VWERASE]; tmp.t_lnextc = tty->termios.c_cc[VLNEXT]; - mutex_unlock(&tty->termios_mutex); + up_read(&tty->termios_rwsem); return copy_to_user(ltchars, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -897,7 +897,7 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) if (copy_from_user(&tmp, ltchars, sizeof(tmp))) return -EFAULT; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); tty->termios.c_cc[VSUSP] = tmp.t_suspc; /* what is dsuspc anyway? */ tty->termios.c_cc[VEOL2] = tmp.t_dsuspc; @@ -906,7 +906,7 @@ static int set_ltchars(struct tty_struct *tty, struct ltchars __user *ltchars) tty->termios.c_cc[VEOL2] = tmp.t_flushc; tty->termios.c_cc[VWERASE] = tmp.t_werasc; tty->termios.c_cc[VLNEXT] = tmp.t_lnextc; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return 0; } #endif @@ -946,7 +946,7 @@ static int send_prio_char(struct tty_struct *tty, char ch) * @arg: enable/disable CLOCAL * * Perform a change to the CLOCAL state and call into the driver - * layer to make it visible. All done with the termios mutex + * layer to make it visible. All done with the termios rwsem */ static int tty_change_softcar(struct tty_struct *tty, int arg) @@ -955,7 +955,7 @@ static int tty_change_softcar(struct tty_struct *tty, int arg) int bit = arg ? CLOCAL : 0; struct ktermios old; - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); old = tty->termios; tty->termios.c_cflag &= ~CLOCAL; tty->termios.c_cflag |= bit; @@ -963,7 +963,7 @@ static int tty_change_softcar(struct tty_struct *tty, int arg) tty->ops->set_termios(tty, &old); if ((tty->termios.c_cflag & CLOCAL) != bit) ret = -EINVAL; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); return ret; } @@ -1066,9 +1066,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, if (user_termios_to_kernel_termios(&kterm, (struct termios __user *) arg)) return -EFAULT; - mutex_lock(&real_tty->termios_mutex); + down_write(&real_tty->termios_rwsem); real_tty->termios_locked = kterm; - mutex_unlock(&real_tty->termios_mutex); + up_write(&real_tty->termios_rwsem); return 0; #else case TIOCGLCKTRMIOS: @@ -1083,9 +1083,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, if (user_termios_to_kernel_termios_1(&kterm, (struct termios __user *) arg)) return -EFAULT; - mutex_lock(&real_tty->termios_mutex); + down_write(&real_tty->termios_rwsem); real_tty->termios_locked = kterm; - mutex_unlock(&real_tty->termios_mutex); + up_write(&real_tty->termios_rwsem); return ret; #endif #ifdef TCGETX @@ -1093,9 +1093,9 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, struct termiox ktermx; if (real_tty->termiox == NULL) return -EINVAL; - mutex_lock(&real_tty->termios_mutex); + down_read(&real_tty->termios_rwsem); memcpy(&ktermx, real_tty->termiox, sizeof(struct termiox)); - mutex_unlock(&real_tty->termios_mutex); + up_read(&real_tty->termios_rwsem); if (copy_to_user(p, &ktermx, sizeof(struct termiox))) ret = -EFAULT; return ret; diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index e52d61573553..6458e11e8e9d 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -415,14 +415,14 @@ EXPORT_SYMBOL_GPL(tty_ldisc_flush); * they are not on hot paths so a little discipline won't do * any harm. * - * Locking: takes termios_mutex + * Locking: takes termios_rwsem */ static void tty_set_termios_ldisc(struct tty_struct *tty, int num) { - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); tty->termios.c_line = num; - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); } /** @@ -602,11 +602,11 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) static void tty_reset_termios(struct tty_struct *tty) { - mutex_lock(&tty->termios_mutex); + down_write(&tty->termios_rwsem); tty->termios = tty->driver->init_termios; tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios); tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios); - mutex_unlock(&tty->termios_mutex); + up_write(&tty->termios_rwsem); } diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index c677829baa8b..02af6ccefe6a 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -828,7 +828,7 @@ static inline int resize_screen(struct vc_data *vc, int width, int height, * If the caller passes a tty structure then update the termios winsize * information and perform any necessary signal handling. * - * Caller must hold the console semaphore. Takes the termios mutex and + * Caller must hold the console semaphore. Takes the termios rwsem and * ctrl_lock of the tty IFF a tty is passed. */ @@ -972,7 +972,7 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows) * the actual work. * * Takes the console sem and the called methods then take the tty - * termios_mutex and the tty ctrl_lock in that order. + * termios_rwsem and the tty ctrl_lock in that order. */ static int vt_resize(struct tty_struct *tty, struct winsize *ws) { diff --git a/include/linux/tty.h b/include/linux/tty.h index 8323ee4f95b9..d3042076d163 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -10,6 +10,7 @@ #include #include #include +#include @@ -243,9 +244,9 @@ struct tty_struct { struct mutex atomic_write_lock; struct mutex legacy_mutex; - struct mutex termios_mutex; + struct rw_semaphore termios_rwsem; spinlock_t ctrl_lock; - /* Termios values are protected by the termios mutex */ + /* Termios values are protected by the termios rwsem */ struct ktermios termios, termios_locked; struct termiox *termiox; /* May be NULL for unsupported */ char name[64]; @@ -253,7 +254,7 @@ struct tty_struct { struct pid *session; unsigned long flags; int count; - struct winsize winsize; /* termios mutex */ + struct winsize winsize; /* termios rwsem */ unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1; unsigned char ctrl_status; /* ctrl_lock */ unsigned int receive_room; /* Bytes free for queue */ -- cgit v1.2.3-71-gd317 From d8c1f929aa8164cd8eaa830068d2fa3159c0764a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:14:31 -0400 Subject: tty: Only guarantee termios read safety for throttle/unthrottle No tty driver modifies termios during throttle() or unthrottle(). Therefore, only read safety is required. However, tty_throttle_safe and tty_unthrottle_safe must still be mutually exclusive; introduce throttle_mutex for that purpose. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 4 ---- drivers/tty/tty_io.c | 1 + drivers/tty/tty_ioctl.c | 8 ++++---- include/linux/tty.h | 1 + 4 files changed, 6 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index d0c8805d8131..b78ee464bd09 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1518,9 +1518,7 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, tty_set_flow_change(tty, TTY_THROTTLE_SAFE); if (receive_room(tty) >= TTY_THRESHOLD_THROTTLE) break; - up_read(&tty->termios_rwsem); throttled = tty_throttle_safe(tty); - down_read(&tty->termios_rwsem); if (!throttled) break; } @@ -2086,9 +2084,7 @@ do_it_again: if (!tty->count) break; n_tty_set_room(tty); - up_read(&tty->termios_rwsem); unthrottled = tty_unthrottle_safe(tty); - down_read(&tty->termios_rwsem); if (!unthrottled) break; } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 639e24ade9bf..1b32da6f6cdd 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3015,6 +3015,7 @@ void initialize_tty_struct(struct tty_struct *tty, tty->session = NULL; tty->pgrp = NULL; mutex_init(&tty->legacy_mutex); + mutex_init(&tty->throttle_mutex); init_rwsem(&tty->termios_rwsem); init_ldsem(&tty->ldisc_sem); init_waitqueue_head(&tty->write_wait); diff --git a/drivers/tty/tty_ioctl.c b/drivers/tty/tty_ioctl.c index 9ce20df8a2c8..03ba081c5772 100644 --- a/drivers/tty/tty_ioctl.c +++ b/drivers/tty/tty_ioctl.c @@ -151,7 +151,7 @@ int tty_throttle_safe(struct tty_struct *tty) { int ret = 0; - down_write(&tty->termios_rwsem); + mutex_lock(&tty->throttle_mutex); if (!test_bit(TTY_THROTTLED, &tty->flags)) { if (tty->flow_change != TTY_THROTTLE_SAFE) ret = 1; @@ -161,7 +161,7 @@ int tty_throttle_safe(struct tty_struct *tty) tty->ops->throttle(tty); } } - up_write(&tty->termios_rwsem); + mutex_unlock(&tty->throttle_mutex); return ret; } @@ -182,7 +182,7 @@ int tty_unthrottle_safe(struct tty_struct *tty) { int ret = 0; - down_write(&tty->termios_rwsem); + mutex_lock(&tty->throttle_mutex); if (test_bit(TTY_THROTTLED, &tty->flags)) { if (tty->flow_change != TTY_UNTHROTTLE_SAFE) ret = 1; @@ -192,7 +192,7 @@ int tty_unthrottle_safe(struct tty_struct *tty) tty->ops->unthrottle(tty); } } - up_write(&tty->termios_rwsem); + mutex_unlock(&tty->throttle_mutex); return ret; } diff --git a/include/linux/tty.h b/include/linux/tty.h index d3042076d163..57a70d1d0412 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -244,6 +244,7 @@ struct tty_struct { struct mutex atomic_write_lock; struct mutex legacy_mutex; + struct mutex throttle_mutex; struct rw_semaphore termios_rwsem; spinlock_t ctrl_lock; /* Termios values are protected by the termios rwsem */ -- cgit v1.2.3-71-gd317 From 1fc359fc3ea72314cc3ebdfa94c60e020c152cd2 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:01 -0400 Subject: tty: Compute flip buffer ptrs The char_buf_ptr and flag_buf_ptr values are trivially derived from the .data field offset; compute values as needed. Fixes a long-standing type-mismatch with the char and flag ptrs. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 22 ++++++++++------------ include/linux/tty.h | 12 ++++++++++-- include/linux/tty_flip.h | 4 ++-- 3 files changed, 22 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index ff1b2e37c3ca..170674cb68fc 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -71,8 +71,6 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) p->next = NULL; p->commit = 0; p->read = 0; - p->char_buf_ptr = (char *)(p->data); - p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; port->buf.memory_used += size; return p; } @@ -265,8 +263,8 @@ int tty_insert_flip_string_fixed_flag(struct tty_port *port, if (unlikely(space == 0)) { break; } - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memset(tb->flag_buf_ptr + tb->used, flag, space); + memcpy(char_buf_ptr(tb, tb->used), chars, space); + memset(flag_buf_ptr(tb, tb->used), flag, space); tb->used += space; copied += space; chars += space; @@ -303,8 +301,8 @@ int tty_insert_flip_string_flags(struct tty_port *port, if (unlikely(space == 0)) { break; } - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memcpy(tb->flag_buf_ptr + tb->used, flags, space); + memcpy(char_buf_ptr(tb, tb->used), chars, space); + memcpy(flag_buf_ptr(tb, tb->used), flags, space); tb->used += space; copied += space; chars += space; @@ -364,8 +362,8 @@ int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars, int space = tty_buffer_request_room(port, size); if (likely(space)) { struct tty_buffer *tb = port->buf.tail; - *chars = tb->char_buf_ptr + tb->used; - memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); + *chars = char_buf_ptr(tb, tb->used); + memset(flag_buf_ptr(tb, tb->used), TTY_NORMAL, space); tb->used += space; } return space; @@ -394,8 +392,8 @@ int tty_prepare_flip_string_flags(struct tty_port *port, int space = tty_buffer_request_room(port, size); if (likely(space)) { struct tty_buffer *tb = port->buf.tail; - *chars = tb->char_buf_ptr + tb->used; - *flags = tb->flag_buf_ptr + tb->used; + *chars = char_buf_ptr(tb, tb->used); + *flags = flag_buf_ptr(tb, tb->used); tb->used += space; } return space; @@ -407,8 +405,8 @@ static int receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) { struct tty_ldisc *disc = tty->ldisc; - char *p = head->char_buf_ptr + head->read; - unsigned char *f = head->flag_buf_ptr + head->read; + unsigned char *p = char_buf_ptr(head, head->read); + char *f = flag_buf_ptr(head, head->read); if (disc->ops->receive_buf2) count = disc->ops->receive_buf2(tty, p, f, count); diff --git a/include/linux/tty.h b/include/linux/tty.h index 57a70d1d0412..87bbaa31ebf5 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -31,8 +31,6 @@ struct tty_buffer { struct tty_buffer *next; - char *char_buf_ptr; - unsigned char *flag_buf_ptr; int used; int size; int commit; @@ -41,6 +39,16 @@ struct tty_buffer { unsigned long data[0]; }; +static inline unsigned char *char_buf_ptr(struct tty_buffer *b, int ofs) +{ + return ((unsigned char *)b->data) + ofs; +} + +static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs) +{ + return (char *)char_buf_ptr(b, ofs) + b->size; +} + /* * We default to dicing tty buffer allocations to this many characters * in order to avoid multiple page allocations. We know the size of diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index e0f252633b47..ad0303994c32 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -18,8 +18,8 @@ static inline int tty_insert_flip_char(struct tty_port *port, { struct tty_buffer *tb = port->buf.tail; if (tb && tb->used < tb->size) { - tb->flag_buf_ptr[tb->used] = flag; - tb->char_buf_ptr[tb->used++] = ch; + *flag_buf_ptr(tb, tb->used) = flag; + *char_buf_ptr(tb, tb->used++) = ch; return 1; } return tty_insert_flip_string_flags(port, &ch, &flag, 1); -- cgit v1.2.3-71-gd317 From 809850b7a5fcc0a96d023e1171a7944c60fd5a71 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:06 -0400 Subject: tty: Use lockless flip buffer free list In preparation for lockless flip buffers, make the flip buffer free list lockless. NB: using llist is not the optimal solution, as the driver and buffer work may contend over the llist head unnecessarily. However, test measurements indicate this contention is low. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 29 ++++++++++++----------------- include/linux/llist.h | 23 +++++++++++++++++++++++ include/linux/tty.h | 8 ++++++-- 3 files changed, 41 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 0259a766b875..069640e5b9c0 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -44,16 +44,17 @@ static void tty_buffer_reset(struct tty_buffer *p, size_t size) void tty_buffer_free_all(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - struct tty_buffer *p; + struct tty_buffer *p, *next; + struct llist_node *llist; while ((p = buf->head) != NULL) { buf->head = p->next; kfree(p); } - while ((p = buf->free) != NULL) { - buf->free = p->next; + llist = llist_del_all(&buf->free); + llist_for_each_entry_safe(p, next, llist, free) kfree(p); - } + buf->tail = NULL; buf->memory_used = 0; } @@ -68,22 +69,20 @@ void tty_buffer_free_all(struct tty_port *port) * allocation behaviour. * Return NULL if out of memory or the allocation would exceed the * per device queue - * - * Locking: Caller must hold tty->buf.lock */ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) { - struct tty_buffer **tbh = &port->buf.free; + struct llist_node *free; struct tty_buffer *p; /* Round the buffer size out */ size = __ALIGN_MASK(size, TTYB_ALIGN_MASK); if (size <= MIN_TTYB_SIZE) { - if (*tbh) { - p = *tbh; - *tbh = p->next; + free = llist_del_first(&port->buf.free); + if (free) { + p = llist_entry(free, struct tty_buffer, free); goto found; } } @@ -109,8 +108,6 @@ found: * * Free a tty buffer, or add it to the free list according to our * internal strategy - * - * Locking: Caller must hold tty->buf.lock */ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) @@ -123,10 +120,8 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) if (b->size > MIN_TTYB_SIZE) kfree(b); - else { - b->next = buf->free; - buf->free = b; - } + else + llist_add(&b->free, &buf->free); } /** @@ -542,7 +537,7 @@ void tty_buffer_init(struct tty_port *port) spin_lock_init(&buf->lock); buf->head = NULL; buf->tail = NULL; - buf->free = NULL; + init_llist_head(&buf->free); buf->memory_used = 0; INIT_WORK(&buf->work, flush_to_ldisc); } diff --git a/include/linux/llist.h b/include/linux/llist.h index cdaa7f023899..8828a78dec9a 100644 --- a/include/linux/llist.h +++ b/include/linux/llist.h @@ -124,6 +124,29 @@ static inline void init_llist_head(struct llist_head *list) &(pos)->member != NULL; \ (pos) = llist_entry((pos)->member.next, typeof(*(pos)), member)) +/** + * llist_for_each_entry_safe - iterate over some deleted entries of lock-less list of given type + * safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @node: the first entry of deleted list entries. + * @member: the name of the llist_node with the struct. + * + * In general, some entries of the lock-less list can be traversed + * safely only after being removed from list, so start with an entry + * instead of list head. + * + * If being used on entries deleted from lock-less list directly, the + * traverse order is from the newest to the oldest added entry. If + * you want to traverse from the oldest to the newest, you must + * reverse the order by yourself before traversing. + */ +#define llist_for_each_entry_safe(pos, n, node, member) \ + for (pos = llist_entry((node), typeof(*pos), member); \ + &pos->member != NULL && \ + (n = llist_entry(pos->member.next, typeof(*n), member), true); \ + pos = n) + /** * llist_empty - tests whether a lock-less list is empty * @head: the list to test diff --git a/include/linux/tty.h b/include/linux/tty.h index 87bbaa31ebf5..5043b12f23ea 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -11,6 +11,7 @@ #include #include #include +#include @@ -30,7 +31,10 @@ #define __DISABLED_CHAR '\0' struct tty_buffer { - struct tty_buffer *next; + union { + struct tty_buffer *next; + struct llist_node free; + }; int used; int size; int commit; @@ -65,7 +69,7 @@ struct tty_bufhead { spinlock_t lock; struct tty_buffer *head; /* Queue head */ struct tty_buffer *tail; /* Active buffer */ - struct tty_buffer *free; /* Free queue head */ + struct llist_head free; /* Free queue head */ int memory_used; /* Buffer space used excluding free queue */ }; -- cgit v1.2.3-71-gd317 From 7391ee16950e772076d321792d9fbf030f921345 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:07 -0400 Subject: tty: Simplify flip buffer list with 0-sized sentinel Use a 0-sized sentinel to avoid assigning the head ptr from the driver side thread. This also eliminates testing head/tail for NULL. When the sentinel is first 'consumed' by the buffer work (or by tty_buffer_flush()), it is detached from the list but not freed nor added to the free list. Both buffer work and tty_buffer_flush() continue to preserve at least 1 flip buffer to which head & tail is pointed. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 49 ++++++++++++++++++------------------------------ include/linux/tty.h | 1 + 2 files changed, 19 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 069640e5b9c0..231b7a8710f1 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -49,13 +49,16 @@ void tty_buffer_free_all(struct tty_port *port) while ((p = buf->head) != NULL) { buf->head = p->next; - kfree(p); + if (p->size > 0) + kfree(p); } llist = llist_del_all(&buf->free); llist_for_each_entry_safe(p, next, llist, free) kfree(p); - buf->tail = NULL; + tty_buffer_reset(&buf->sentinel, 0); + buf->head = &buf->sentinel; + buf->tail = &buf->sentinel; buf->memory_used = 0; } @@ -120,7 +123,7 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) if (b->size > MIN_TTYB_SIZE) kfree(b); - else + else if (b->size > 0) llist_add(&b->free, &buf->free); } @@ -140,8 +143,6 @@ static void __tty_buffer_flush(struct tty_port *port) struct tty_bufhead *buf = &port->buf; struct tty_buffer *next; - if (unlikely(buf->head == NULL)) - return; while ((next = buf->head->next) != NULL) { tty_buffer_free(port, buf->head); buf->head = next; @@ -200,23 +201,14 @@ int tty_buffer_request_room(struct tty_port *port, size_t size) int left; unsigned long flags; spin_lock_irqsave(&buf->lock, flags); - /* OPTIMISATION: We could keep a per tty "zero" sized buffer to - remove this conditional if its worth it. This would be invisible - to the callers */ b = buf->tail; - if (b != NULL) - left = b->size - b->used; - else - left = 0; + left = b->size - b->used; if (left < size) { /* This is the slow path - looking for new buffers to use */ if ((n = tty_buffer_alloc(port, size)) != NULL) { - if (b != NULL) { - b->next = n; - b->commit = b->used; - } else - buf->head = n; + b->next = n; + b->commit = b->used; buf->tail = n; } else size = left; @@ -247,10 +239,8 @@ int tty_insert_flip_string_fixed_flag(struct tty_port *port, int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); int space = tty_buffer_request_room(port, goal); struct tty_buffer *tb = port->buf.tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) { + if (unlikely(space == 0)) break; - } memcpy(char_buf_ptr(tb, tb->used), chars, space); memset(flag_buf_ptr(tb, tb->used), flag, space); tb->used += space; @@ -285,10 +275,8 @@ int tty_insert_flip_string_flags(struct tty_port *port, int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE); int space = tty_buffer_request_room(port, goal); struct tty_buffer *tb = port->buf.tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) { + if (unlikely(space == 0)) break; - } memcpy(char_buf_ptr(tb, tb->used), chars, space); memcpy(flag_buf_ptr(tb, tb->used), flags, space); tb->used += space; @@ -322,8 +310,7 @@ void tty_schedule_flip(struct tty_port *port) WARN_ON(port->low_latency); spin_lock_irqsave(&buf->lock, flags); - if (buf->tail != NULL) - buf->tail->commit = buf->tail->used; + buf->tail->commit = buf->tail->used; spin_unlock_irqrestore(&buf->lock, flags); schedule_work(&buf->work); } @@ -438,8 +425,8 @@ static void flush_to_ldisc(struct work_struct *work) spin_lock_irqsave(&buf->lock, flags); if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { - struct tty_buffer *head; - while ((head = buf->head) != NULL) { + while (1) { + struct tty_buffer *head = buf->head; int count; count = head->commit - head->read; @@ -509,8 +496,7 @@ void tty_flip_buffer_push(struct tty_port *port) unsigned long flags; spin_lock_irqsave(&buf->lock, flags); - if (buf->tail != NULL) - buf->tail->commit = buf->tail->used; + buf->tail->commit = buf->tail->used; spin_unlock_irqrestore(&buf->lock, flags); if (port->low_latency) @@ -535,8 +521,9 @@ void tty_buffer_init(struct tty_port *port) struct tty_bufhead *buf = &port->buf; spin_lock_init(&buf->lock); - buf->head = NULL; - buf->tail = NULL; + tty_buffer_reset(&buf->sentinel, 0); + buf->head = &buf->sentinel; + buf->tail = &buf->sentinel; init_llist_head(&buf->free); buf->memory_used = 0; INIT_WORK(&buf->work, flush_to_ldisc); diff --git a/include/linux/tty.h b/include/linux/tty.h index 5043b12f23ea..2e93eb831c61 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -67,6 +67,7 @@ static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs) struct tty_bufhead { struct work_struct work; spinlock_t lock; + struct tty_buffer sentinel; struct tty_buffer *head; /* Queue head */ struct tty_buffer *tail; /* Active buffer */ struct llist_head free; /* Free queue head */ -- cgit v1.2.3-71-gd317 From 7bfe0b7116be207cf2204ae06335cc89d8f8ee02 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:08 -0400 Subject: tty: Track flip buffer memory limit atomically Lockless flip buffers require atomically updating the bytes-in-use watermark. The pty driver also peeks at the watermark value to limit memory consumption to a much lower value than the default; query the watermark with new fn, tty_buffer_space_avail(). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 10 +++------- drivers/tty/tty_buffer.c | 37 +++++++++++++++++++++++++++++++------ include/linux/tty.h | 3 +-- include/linux/tty_flip.h | 1 + 4 files changed, 36 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 1b39dd639ee9..b38a28bd9511 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -89,17 +89,13 @@ static void pty_unthrottle(struct tty_struct *tty) * pty_space - report space left for writing * @to: tty we are writing into * - * The tty buffers allow 64K but we sneak a peak and clip at 8K this - * allows a lot of overspill room for echo and other fun messes to - * be handled properly + * Limit the buffer space used by ptys to 8k. */ static int pty_space(struct tty_struct *to) { - int n = 8192 - to->port->buf.memory_used; - if (n < 0) - return 0; - return n; + int n = tty_buffer_space_avail(to->port); + return min(n, 8192); } /** diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 231b7a8710f1..5d5a56407aa8 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -22,6 +22,31 @@ #define MIN_TTYB_SIZE 256 #define TTYB_ALIGN_MASK 255 +/* + * Byte threshold to limit memory consumption for flip buffers. + * The actual memory limit is > 2x this amount. + */ +#define TTYB_MEM_LIMIT 65536 + + +/** + * tty_buffer_space_avail - return unused buffer space + * @port - tty_port owning the flip buffer + * + * Returns the # of bytes which can be written by the driver without + * reaching the buffer limit. + * + * Note: this does not guarantee that memory is available to write + * the returned # of bytes (use tty_prepare_flip_string_xxx() to + * pre-allocate if memory guarantee is required). + */ + +int tty_buffer_space_avail(struct tty_port *port) +{ + int space = TTYB_MEM_LIMIT - atomic_read(&port->buf.memory_used); + return max(space, 0); +} + static void tty_buffer_reset(struct tty_buffer *p, size_t size) { p->used = 0; @@ -59,7 +84,8 @@ void tty_buffer_free_all(struct tty_port *port) tty_buffer_reset(&buf->sentinel, 0); buf->head = &buf->sentinel; buf->tail = &buf->sentinel; - buf->memory_used = 0; + + atomic_set(&buf->memory_used, 0); } /** @@ -92,7 +118,7 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) /* Should possibly check if this fails for the largest buffer we have queued and recycle that ? */ - if (port->buf.memory_used + size > 65536) + if (atomic_read(&port->buf.memory_used) > TTYB_MEM_LIMIT) return NULL; p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); if (p == NULL) @@ -100,7 +126,7 @@ static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size) found: tty_buffer_reset(p, size); - port->buf.memory_used += size; + atomic_add(size, &port->buf.memory_used); return p; } @@ -118,8 +144,7 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) struct tty_bufhead *buf = &port->buf; /* Dumb strategy for now - should keep some stats */ - buf->memory_used -= b->size; - WARN_ON(buf->memory_used < 0); + WARN_ON(atomic_sub_return(b->size, &buf->memory_used) < 0); if (b->size > MIN_TTYB_SIZE) kfree(b); @@ -525,7 +550,7 @@ void tty_buffer_init(struct tty_port *port) buf->head = &buf->sentinel; buf->tail = &buf->sentinel; init_llist_head(&buf->free); - buf->memory_used = 0; + atomic_set(&buf->memory_used, 0); INIT_WORK(&buf->work, flush_to_ldisc); } diff --git a/include/linux/tty.h b/include/linux/tty.h index 2e93eb831c61..7c124541f011 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -71,8 +71,7 @@ struct tty_bufhead { struct tty_buffer *head; /* Queue head */ struct tty_buffer *tail; /* Active buffer */ struct llist_head free; /* Free queue head */ - int memory_used; /* Buffer space used excluding - free queue */ + atomic_t memory_used; /* In-use buffers excluding free list */ }; /* * When a break, frame error, or parity error happens, these codes are diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index ad0303994c32..6944ed2ce692 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -1,6 +1,7 @@ #ifndef _LINUX_TTY_FLIP_H #define _LINUX_TTY_FLIP_H +extern int tty_buffer_space_avail(struct tty_port *port); extern int tty_buffer_request_room(struct tty_port *port, size_t size); extern int tty_insert_flip_string_flags(struct tty_port *port, const unsigned char *chars, const char *flags, size_t size); -- cgit v1.2.3-71-gd317 From e9975fdec0138f1b2a85b9624e41660abd9865d4 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:10 -0400 Subject: tty: Ensure single-threaded flip buffer consumer with mutex The buffer work may race with parallel tty_buffer_flush. Use a mutex to guarantee exclusive modify access to the head flip buffer. Remove the unneeded spin lock. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/staging/dgrp/dgrp_tty.c | 2 ++ drivers/tty/tty_buffer.c | 40 +++++++++++++++++++--------------------- include/linux/tty.h | 2 +- 3 files changed, 22 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/staging/dgrp/dgrp_tty.c b/drivers/staging/dgrp/dgrp_tty.c index 654f6010b473..0d52de3729c6 100644 --- a/drivers/staging/dgrp/dgrp_tty.c +++ b/drivers/staging/dgrp/dgrp_tty.c @@ -1120,7 +1120,9 @@ static void dgrp_tty_close(struct tty_struct *tty, struct file *file) if (!sent_printer_offstr) dgrp_tty_flush_buffer(tty); + spin_unlock_irqrestore(&nd->nd_lock, lock_flags); tty_ldisc_flush(tty); + spin_lock_irqsave(&nd->nd_lock, lock_flags); break; } diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 685757c6ce87..c3c606c52722 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -157,8 +157,6 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) * flush all the buffers containing receive data. Caller must * hold the buffer lock and must have ensured no parallel flush to * ldisc is running. - * - * Locking: Caller must hold tty->buf.lock */ static void __tty_buffer_flush(struct tty_port *port) @@ -182,29 +180,29 @@ static void __tty_buffer_flush(struct tty_port *port) * being processed by flush_to_ldisc then we defer the processing * to that function * - * Locking: none + * Locking: takes flush_mutex to ensure single-threaded flip buffer + * 'consumer' */ void tty_buffer_flush(struct tty_struct *tty) { struct tty_port *port = tty->port; struct tty_bufhead *buf = &port->buf; - unsigned long flags; - - spin_lock_irqsave(&buf->lock, flags); + mutex_lock(&buf->flush_mutex); /* If the data is being pushed to the tty layer then we can't process it here. Instead set a flag and the flush_to_ldisc path will process the flush request before it exits */ if (test_bit(TTYP_FLUSHING, &port->iflags)) { set_bit(TTYP_FLUSHPENDING, &port->iflags); - spin_unlock_irqrestore(&buf->lock, flags); + mutex_unlock(&buf->flush_mutex); wait_event(tty->read_wait, test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); return; - } else - __tty_buffer_flush(port); - spin_unlock_irqrestore(&buf->lock, flags); + } + + __tty_buffer_flush(port); + mutex_unlock(&buf->flush_mutex); } /** @@ -408,9 +406,10 @@ receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) * This routine is called out of the software interrupt to flush data * from the buffer chain to the line discipline. * - * Locking: holds tty->buf.lock to guard buffer list. Drops the lock - * while invoking the line discipline receive_buf method. The - * receive_buf method is single threaded for each tty instance. + * The receive_buf method is single threaded for each tty instance. + * + * Locking: takes flush_mutex to ensure single-threaded flip buffer + * 'consumer' */ static void flush_to_ldisc(struct work_struct *work) @@ -418,7 +417,6 @@ static void flush_to_ldisc(struct work_struct *work) struct tty_port *port = container_of(work, struct tty_port, buf.work); struct tty_bufhead *buf = &port->buf; struct tty_struct *tty; - unsigned long flags; struct tty_ldisc *disc; tty = port->itty; @@ -429,7 +427,7 @@ static void flush_to_ldisc(struct work_struct *work) if (disc == NULL) return; - spin_lock_irqsave(&buf->lock, flags); + mutex_lock(&buf->flush_mutex); if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { while (1) { @@ -444,11 +442,13 @@ static void flush_to_ldisc(struct work_struct *work) tty_buffer_free(port, head); continue; } - spin_unlock_irqrestore(&buf->lock, flags); + + mutex_unlock(&buf->flush_mutex); count = receive_buf(tty, head, count); - spin_lock_irqsave(&buf->lock, flags); + mutex_lock(&buf->flush_mutex); + /* Ldisc or user is trying to flush the buffers. We may have a deferred request to flush the input buffer, if so pull the chain under the lock @@ -464,7 +464,7 @@ static void flush_to_ldisc(struct work_struct *work) clear_bit(TTYP_FLUSHING, &port->iflags); } - spin_unlock_irqrestore(&buf->lock, flags); + mutex_unlock(&buf->flush_mutex); tty_ldisc_deref(disc); } @@ -514,15 +514,13 @@ EXPORT_SYMBOL(tty_flip_buffer_push); * * Set up the initial state of the buffer management for a tty device. * Must be called before the other tty buffer functions are used. - * - * Locking: none */ void tty_buffer_init(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - spin_lock_init(&buf->lock); + mutex_init(&buf->flush_mutex); tty_buffer_reset(&buf->sentinel, 0); buf->head = &buf->sentinel; buf->tail = &buf->sentinel; diff --git a/include/linux/tty.h b/include/linux/tty.h index 7c124541f011..1c8fef0e3ff6 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -66,7 +66,7 @@ static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs) struct tty_bufhead { struct work_struct work; - spinlock_t lock; + struct mutex flush_mutex; struct tty_buffer sentinel; struct tty_buffer *head; /* Queue head */ struct tty_buffer *tail; /* Active buffer */ -- cgit v1.2.3-71-gd317 From d7a68be4f265be10e24be931c257af30ca55566b Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:11 -0400 Subject: tty: Only perform flip buffer flush from tty_buffer_flush() Now that dropping the buffer lock is not necessary (as result of converting the spin lock to a mutex), the flip buffer flush no longer needs to be handled by the buffer work. Simply signal a flush is required; the buffer work will exit the i/o loop, which allows tty_buffer_flush() to proceed. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 63 ++++++++++++++++-------------------------------- include/linux/tty.h | 1 - 2 files changed, 21 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index c3c606c52722..39cae611fe59 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -189,19 +189,11 @@ void tty_buffer_flush(struct tty_struct *tty) struct tty_port *port = tty->port; struct tty_bufhead *buf = &port->buf; - mutex_lock(&buf->flush_mutex); - /* If the data is being pushed to the tty layer then we can't - process it here. Instead set a flag and the flush_to_ldisc - path will process the flush request before it exits */ - if (test_bit(TTYP_FLUSHING, &port->iflags)) { - set_bit(TTYP_FLUSHPENDING, &port->iflags); - mutex_unlock(&buf->flush_mutex); - wait_event(tty->read_wait, - test_bit(TTYP_FLUSHPENDING, &port->iflags) == 0); - return; - } + set_bit(TTYP_FLUSHPENDING, &port->iflags); + mutex_lock(&buf->flush_mutex); __tty_buffer_flush(port); + clear_bit(TTYP_FLUSHPENDING, &port->iflags); mutex_unlock(&buf->flush_mutex); } @@ -429,39 +421,26 @@ static void flush_to_ldisc(struct work_struct *work) mutex_lock(&buf->flush_mutex); - if (!test_and_set_bit(TTYP_FLUSHING, &port->iflags)) { - while (1) { - struct tty_buffer *head = buf->head; - int count; - - count = head->commit - head->read; - if (!count) { - if (head->next == NULL) - break; - buf->head = head->next; - tty_buffer_free(port, head); - continue; - } - - mutex_unlock(&buf->flush_mutex); - - count = receive_buf(tty, head, count); - - mutex_lock(&buf->flush_mutex); - - /* Ldisc or user is trying to flush the buffers. - We may have a deferred request to flush the - input buffer, if so pull the chain under the lock - and empty the queue */ - if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) { - __tty_buffer_flush(port); - clear_bit(TTYP_FLUSHPENDING, &port->iflags); - wake_up(&tty->read_wait); - break; - } else if (!count) + while (1) { + struct tty_buffer *head = buf->head; + int count; + + /* Ldisc or user is trying to flush the buffers. */ + if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) + break; + + count = head->commit - head->read; + if (!count) { + if (head->next == NULL) break; + buf->head = head->next; + tty_buffer_free(port, head); + continue; } - clear_bit(TTYP_FLUSHING, &port->iflags); + + count = receive_buf(tty, head, count); + if (!count) + break; } mutex_unlock(&buf->flush_mutex); diff --git a/include/linux/tty.h b/include/linux/tty.h index 1c8fef0e3ff6..1d5bacca3652 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -213,7 +213,6 @@ struct tty_port { wait_queue_head_t delta_msr_wait; /* Modem status change */ unsigned long flags; /* TTY flags ASY_*/ unsigned long iflags; /* TTYP_ internal flags */ -#define TTYP_FLUSHING 1 /* Flushing to ldisc in progress */ #define TTYP_FLUSHPENDING 2 /* Queued buffer flush pending */ unsigned char console:1, /* port is a console */ low_latency:1; /* direct buffer flush */ -- cgit v1.2.3-71-gd317 From 8c1fb49ba107c7db9441ef6ec0ab5830d112cc2a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:12 -0400 Subject: tty: Avoid false-sharing flip buffer ptrs Separate the head and tail ptrs to avoid cache-line contention (so called 'false-sharing') between concurrent threads. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- include/linux/tty.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/tty.h b/include/linux/tty.h index 1d5bacca3652..b8e8adf95bf3 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -65,13 +65,13 @@ static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs) struct tty_bufhead { + struct tty_buffer *head; /* Queue head */ struct work_struct work; struct mutex flush_mutex; struct tty_buffer sentinel; - struct tty_buffer *head; /* Queue head */ - struct tty_buffer *tail; /* Active buffer */ struct llist_head free; /* Free queue head */ atomic_t memory_used; /* In-use buffers excluding free list */ + struct tty_buffer *tail; /* Active buffer */ }; /* * When a break, frame error, or parity error happens, these codes are -- cgit v1.2.3-71-gd317 From 0f56bd2f6a97d8b0eb5c8f9bc04b83a6c16d1d48 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:13 -0400 Subject: tty: Use non-atomic state to signal flip buffer flush pending Atomic bit ops are no longer required to indicate a flip buffer flush is pending, as the flush_mutex is sufficient barrier. Remove the unnecessary port .iflags field and localize flip buffer state to struct tty_bufhead. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 7 ++++--- include/linux/tty.h | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 39cae611fe59..fb042b9a8d68 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -189,11 +189,11 @@ void tty_buffer_flush(struct tty_struct *tty) struct tty_port *port = tty->port; struct tty_bufhead *buf = &port->buf; - set_bit(TTYP_FLUSHPENDING, &port->iflags); + buf->flushpending = 1; mutex_lock(&buf->flush_mutex); __tty_buffer_flush(port); - clear_bit(TTYP_FLUSHPENDING, &port->iflags); + buf->flushpending = 0; mutex_unlock(&buf->flush_mutex); } @@ -426,7 +426,7 @@ static void flush_to_ldisc(struct work_struct *work) int count; /* Ldisc or user is trying to flush the buffers. */ - if (test_bit(TTYP_FLUSHPENDING, &port->iflags)) + if (buf->flushpending) break; count = head->commit - head->read; @@ -505,6 +505,7 @@ void tty_buffer_init(struct tty_port *port) buf->tail = &buf->sentinel; init_llist_head(&buf->free); atomic_set(&buf->memory_used, 0); + buf->flushpending = 0; INIT_WORK(&buf->work, flush_to_ldisc); } diff --git a/include/linux/tty.h b/include/linux/tty.h index b8e8adf95bf3..991575fe3451 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -68,6 +68,7 @@ struct tty_bufhead { struct tty_buffer *head; /* Queue head */ struct work_struct work; struct mutex flush_mutex; + unsigned int flushpending:1; struct tty_buffer sentinel; struct llist_head free; /* Free queue head */ atomic_t memory_used; /* In-use buffers excluding free list */ @@ -212,8 +213,6 @@ struct tty_port { wait_queue_head_t close_wait; /* Close waiters */ wait_queue_head_t delta_msr_wait; /* Modem status change */ unsigned long flags; /* TTY flags ASY_*/ - unsigned long iflags; /* TTYP_ internal flags */ -#define TTYP_FLUSHPENDING 2 /* Queued buffer flush pending */ unsigned char console:1, /* port is a console */ low_latency:1; /* direct buffer flush */ struct mutex mutex; /* Locking */ -- cgit v1.2.3-71-gd317 From a7c8d58c79853adeebf0a1ddc9c63e433b4d97f1 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:15 -0400 Subject: tty: Fix unsafe vt paste_selection() Convert the tty_buffer_flush() exclusion mechanism to a public interface - tty_buffer_lock/unlock_exclusive() - and use the interface to safely write the paste selection to the line discipline. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 61 ++++++++++++++++++++++++++++++++++++---------- drivers/tty/vt/selection.c | 4 ++- include/linux/tty.h | 4 +-- include/linux/tty_flip.h | 3 +++ 4 files changed, 56 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index dbe4a718e2dc..f22e116db105 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -29,6 +29,42 @@ #define TTYB_MEM_LIMIT 65536 +/** + * tty_buffer_lock_exclusive - gain exclusive access to buffer + * tty_buffer_unlock_exclusive - release exclusive access + * + * @port - tty_port owning the flip buffer + * + * Guarantees safe use of the line discipline's receive_buf() method by + * excluding the buffer work and any pending flush from using the flip + * buffer. Data can continue to be added concurrently to the flip buffer + * from the driver side. + * + * On release, the buffer work is restarted if there is data in the + * flip buffer + */ + +void tty_buffer_lock_exclusive(struct tty_port *port) +{ + struct tty_bufhead *buf = &port->buf; + + atomic_inc(&buf->priority); + mutex_lock(&buf->lock); +} + +void tty_buffer_unlock_exclusive(struct tty_port *port) +{ + struct tty_bufhead *buf = &port->buf; + int restart; + + restart = buf->head->commit != buf->head->read; + + atomic_dec(&buf->priority); + mutex_unlock(&buf->lock); + if (restart) + queue_work(system_unbound_wq, &buf->work); +} + /** * tty_buffer_space_avail - return unused buffer space * @port - tty_port owning the flip buffer @@ -158,7 +194,7 @@ static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b) * being processed by flush_to_ldisc then we defer the processing * to that function * - * Locking: takes flush_mutex to ensure single-threaded flip buffer + * Locking: takes buffer lock to ensure single-threaded flip buffer * 'consumer' */ @@ -168,16 +204,16 @@ void tty_buffer_flush(struct tty_struct *tty) struct tty_bufhead *buf = &port->buf; struct tty_buffer *next; - buf->flushpending = 1; + atomic_inc(&buf->priority); - mutex_lock(&buf->flush_mutex); + mutex_lock(&buf->lock); while ((next = buf->head->next) != NULL) { tty_buffer_free(port, buf->head); buf->head = next; } buf->head->read = buf->head->commit; - buf->flushpending = 0; - mutex_unlock(&buf->flush_mutex); + atomic_dec(&buf->priority); + mutex_unlock(&buf->lock); } /** @@ -383,7 +419,7 @@ receive_buf(struct tty_struct *tty, struct tty_buffer *head, int count) * * The receive_buf method is single threaded for each tty instance. * - * Locking: takes flush_mutex to ensure single-threaded flip buffer + * Locking: takes buffer lock to ensure single-threaded flip buffer * 'consumer' */ @@ -402,14 +438,14 @@ static void flush_to_ldisc(struct work_struct *work) if (disc == NULL) return; - mutex_lock(&buf->flush_mutex); + mutex_lock(&buf->lock); while (1) { struct tty_buffer *head = buf->head; int count; - /* Ldisc or user is trying to flush the buffers. */ - if (buf->flushpending) + /* Ldisc or user is trying to gain exclusive access */ + if (atomic_read(&buf->priority)) break; count = head->commit - head->read; @@ -426,7 +462,7 @@ static void flush_to_ldisc(struct work_struct *work) break; } - mutex_unlock(&buf->flush_mutex); + mutex_unlock(&buf->lock); tty_ldisc_deref(disc); } @@ -482,13 +518,12 @@ void tty_buffer_init(struct tty_port *port) { struct tty_bufhead *buf = &port->buf; - mutex_init(&buf->flush_mutex); + mutex_init(&buf->lock); tty_buffer_reset(&buf->sentinel, 0); buf->head = &buf->sentinel; buf->tail = &buf->sentinel; init_llist_head(&buf->free); atomic_set(&buf->memory_used, 0); - buf->flushpending = 0; + atomic_set(&buf->priority, 0); INIT_WORK(&buf->work, flush_to_ldisc); } - diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 2ca8d6b6514c..ea27804d87af 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -24,6 +24,7 @@ #include #include #include +#include /* Don't take this from : 011-015 on the screen aren't spaces */ #define isspace(c) ((c) == ' ') @@ -346,8 +347,8 @@ int paste_selection(struct tty_struct *tty) console_unlock(); ld = tty_ldisc_ref_wait(tty); + tty_buffer_lock_exclusive(&vc->port); - /* FIXME: this is completely unsafe */ add_wait_queue(&vc->paste_wait, &wait); while (sel_buffer && sel_buffer_lth > pasted) { set_current_state(TASK_INTERRUPTIBLE); @@ -363,6 +364,7 @@ int paste_selection(struct tty_struct *tty) remove_wait_queue(&vc->paste_wait, &wait); __set_current_state(TASK_RUNNING); + tty_buffer_unlock_exclusive(&vc->port); tty_ldisc_deref(ld); return 0; } diff --git a/include/linux/tty.h b/include/linux/tty.h index 991575fe3451..7a9a3b0a6b5a 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -67,8 +67,8 @@ static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs) struct tty_bufhead { struct tty_buffer *head; /* Queue head */ struct work_struct work; - struct mutex flush_mutex; - unsigned int flushpending:1; + struct mutex lock; + atomic_t priority; struct tty_buffer sentinel; struct llist_head free; /* Free queue head */ atomic_t memory_used; /* In-use buffers excluding free list */ diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index 6944ed2ce692..21ddd7d9ea1f 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -32,4 +32,7 @@ static inline int tty_insert_flip_string(struct tty_port *port, return tty_insert_flip_string_fixed_flag(port, chars, TTY_NORMAL, size); } +extern void tty_buffer_lock_exclusive(struct tty_port *port); +extern void tty_buffer_unlock_exclusive(struct tty_port *port); + #endif /* _LINUX_TTY_FLIP_H */ -- cgit v1.2.3-71-gd317 From 9114fe8ccf1871f630d2c14cd60e5f455b015459 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 09:36:16 -0400 Subject: tty: Remove private constant from global namespace TTY_BUFFER_PAGE is only used within drivers/tty/tty_buffer.c; relocate to that file scope. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 10 ++++++++++ include/linux/tty.h | 11 ----------- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index f22e116db105..c043136fbe51 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -28,6 +28,16 @@ */ #define TTYB_MEM_LIMIT 65536 +/* + * We default to dicing tty buffer allocations to this many characters + * in order to avoid multiple page allocations. We know the size of + * tty_buffer itself but it must also be taken into account that the + * the buffer is 256 byte aligned. See tty_buffer_find for the allocation + * logic this must match + */ + +#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF) + /** * tty_buffer_lock_exclusive - gain exclusive access to buffer diff --git a/include/linux/tty.h b/include/linux/tty.h index 7a9a3b0a6b5a..5fd5d6f1ebc4 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -53,17 +53,6 @@ static inline char *flag_buf_ptr(struct tty_buffer *b, int ofs) return (char *)char_buf_ptr(b, ofs) + b->size; } -/* - * We default to dicing tty buffer allocations to this many characters - * in order to avoid multiple page allocations. We know the size of - * tty_buffer itself but it must also be taken into account that the - * the buffer is 256 byte aligned. See tty_buffer_find for the allocation - * logic this must match - */ - -#define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF) - - struct tty_bufhead { struct tty_buffer *head; /* Queue head */ struct work_struct work; -- cgit v1.2.3-71-gd317 From fc423ff00df3a19554414eed80aef9de9b50313e Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 20 Jul 2013 12:13:52 +0200 Subject: team: add peer notification When port is enabled or disabled, allow to notify peers by unsolicitated NAs or gratuitous ARPs. Disabled by default. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/if_team.h | 8 ++++- 2 files changed, 94 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index bff7e0b0b4e7..0433ee994f8c 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -622,6 +622,46 @@ static int team_change_mode(struct team *team, const char *kind) } +/********************* + * Peers notification + *********************/ + +static void team_notify_peers_work(struct work_struct *work) +{ + struct team *team; + + team = container_of(work, struct team, notify_peers.dw.work); + + if (!rtnl_trylock()) { + schedule_delayed_work(&team->notify_peers.dw, 0); + return; + } + call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, team->dev); + rtnl_unlock(); + if (!atomic_dec_and_test(&team->notify_peers.count_pending)) + schedule_delayed_work(&team->notify_peers.dw, + msecs_to_jiffies(team->notify_peers.interval)); +} + +static void team_notify_peers(struct team *team) +{ + if (!team->notify_peers.count || !netif_running(team->dev)) + return; + atomic_set(&team->notify_peers.count_pending, team->notify_peers.count); + schedule_delayed_work(&team->notify_peers.dw, 0); +} + +static void team_notify_peers_init(struct team *team) +{ + INIT_DELAYED_WORK(&team->notify_peers.dw, team_notify_peers_work); +} + +static void team_notify_peers_fini(struct team *team) +{ + cancel_delayed_work_sync(&team->notify_peers.dw); +} + + /************************ * Rx path frame handler ************************/ @@ -846,6 +886,7 @@ static void team_port_enable(struct team *team, team_queue_override_port_add(team, port); if (team->ops.port_enabled) team->ops.port_enabled(team, port); + team_notify_peers(team); } static void __reconstruct_port_hlist(struct team *team, int rm_index) @@ -875,6 +916,7 @@ static void team_port_disable(struct team *team, team->en_port_count--; team_queue_override_port_del(team, port); team_adjust_ops(team); + team_notify_peers(team); } #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ @@ -1205,6 +1247,34 @@ static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx) return team_change_mode(team, ctx->data.str_val); } +static int team_notify_peers_count_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + ctx->data.u32_val = team->notify_peers.count; + return 0; +} + +static int team_notify_peers_count_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + team->notify_peers.count = ctx->data.u32_val; + return 0; +} + +static int team_notify_peers_interval_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + ctx->data.u32_val = team->notify_peers.interval; + return 0; +} + +static int team_notify_peers_interval_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + team->notify_peers.interval = ctx->data.u32_val; + return 0; +} + static int team_port_en_option_get(struct team *team, struct team_gsetter_ctx *ctx) { @@ -1316,6 +1386,18 @@ static const struct team_option team_options[] = { .getter = team_mode_option_get, .setter = team_mode_option_set, }, + { + .name = "notify_peers_count", + .type = TEAM_OPTION_TYPE_U32, + .getter = team_notify_peers_count_get, + .setter = team_notify_peers_count_set, + }, + { + .name = "notify_peers_interval", + .type = TEAM_OPTION_TYPE_U32, + .getter = team_notify_peers_interval_get, + .setter = team_notify_peers_interval_set, + }, { .name = "enabled", .type = TEAM_OPTION_TYPE_BOOL, @@ -1396,6 +1478,9 @@ static int team_init(struct net_device *dev) INIT_LIST_HEAD(&team->option_list); INIT_LIST_HEAD(&team->option_inst_list); + + team_notify_peers_init(team); + err = team_options_register(team, team_options, ARRAY_SIZE(team_options)); if (err) goto err_options_register; @@ -1406,6 +1491,7 @@ static int team_init(struct net_device *dev) return 0; err_options_register: + team_notify_peers_fini(team); team_queue_override_fini(team); err_team_queue_override_init: free_percpu(team->pcpu_stats); @@ -1425,6 +1511,7 @@ static void team_uninit(struct net_device *dev) __team_change_mode(team, NULL); /* cleanup */ __team_options_unregister(team, team_options, ARRAY_SIZE(team_options)); + team_notify_peers_fini(team); team_queue_override_fini(team); mutex_unlock(&team->lock); } diff --git a/include/linux/if_team.h b/include/linux/if_team.h index f6156f91eb1c..b0b83683461e 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -10,9 +10,9 @@ #ifndef _LINUX_IF_TEAM_H_ #define _LINUX_IF_TEAM_H_ - #include #include +#include #include struct team_pcpu_stats { @@ -194,6 +194,12 @@ struct team { bool user_carrier_enabled; bool queue_override_enabled; struct list_head *qom_lists; /* array of queue override mapping lists */ + struct { + unsigned int count; + unsigned int interval; /* in ms */ + atomic_t count_pending; + struct delayed_work dw; + } notify_peers; long mode_priv[TEAM_MODE_PRIV_LONGS]; }; -- cgit v1.2.3-71-gd317 From 4aa5dee4d9997879adff858514844efab5a15a01 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 20 Jul 2013 12:13:53 +0200 Subject: net: convert resend IGMP to notifier event Until now, bond_resend_igmp_join_requests() looks for vlans attached to bonding device, bridge where bonding act as port manually. It does not care of other scenarios, like stacked bonds or team device above. Make this more generic and use netdev notifier to propagate the event to upper devices and to actually call ip_mc_rejoin_groups(). Signed-off-by: Jiri Pirko Acked-by: Veaceslav Falico Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 44 ++++++++------------------------------- drivers/net/team/team.c | 4 ++++ include/linux/igmp.h | 1 - include/linux/netdevice.h | 1 + net/8021q/vlan.c | 1 + net/bridge/br_notify.c | 5 +++++ net/ipv4/igmp.c | 46 +++++++++++++++++++++++++++++++++++------ 7 files changed, 60 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 07f257d44a1e..ae9864c9fa38 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -715,15 +715,6 @@ static int bond_set_allmulti(struct bonding *bond, int inc) return err; } -static void __bond_resend_igmp_join_requests(struct net_device *dev) -{ - struct in_device *in_dev; - - in_dev = __in_dev_get_rcu(dev); - if (in_dev) - ip_mc_rejoin_groups(in_dev); -} - /* * Retrieve the list of registered multicast addresses for the bonding * device and retransmit an IGMP JOIN request to the current active @@ -731,33 +722,12 @@ static void __bond_resend_igmp_join_requests(struct net_device *dev) */ static void bond_resend_igmp_join_requests(struct bonding *bond) { - struct net_device *bond_dev, *vlan_dev, *upper_dev; - struct vlan_entry *vlan; - - read_lock(&bond->lock); - rcu_read_lock(); - - bond_dev = bond->dev; - - /* rejoin all groups on bond device */ - __bond_resend_igmp_join_requests(bond_dev); - - /* - * if bond is enslaved to a bridge, - * then rejoin all groups on its master - */ - upper_dev = netdev_master_upper_dev_get_rcu(bond_dev); - if (upper_dev && upper_dev->priv_flags & IFF_EBRIDGE) - __bond_resend_igmp_join_requests(upper_dev); - - /* rejoin all groups on vlan devices */ - list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { - vlan_dev = __vlan_find_dev_deep(bond_dev, htons(ETH_P_8021Q), - vlan->vlan_id); - if (vlan_dev) - __bond_resend_igmp_join_requests(vlan_dev); + if (!rtnl_trylock()) { + queue_delayed_work(bond->wq, &bond->mcast_work, 0); + return; } - rcu_read_unlock(); + call_netdevice_notifiers(NETDEV_RESEND_IGMP, bond->dev); + rtnl_unlock(); /* We use curr_slave_lock to protect against concurrent access to * igmp_retrans from multiple running instances of this function and @@ -3234,6 +3204,10 @@ static int bond_slave_netdev_event(unsigned long event, case NETDEV_FEAT_CHANGE: bond_compute_features(bond); break; + case NETDEV_RESEND_IGMP: + /* Propagate to master device */ + call_netdevice_notifiers(event, slave->bond->dev); + break; default: break; } diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 0433ee994f8c..2587dc86d533 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -2785,6 +2785,10 @@ static int team_device_event(struct notifier_block *unused, case NETDEV_PRE_TYPE_CHANGE: /* Forbid to change type of underlaying device */ return NOTIFY_BAD; + case NETDEV_RESEND_IGMP: + /* Propagate to master device */ + call_netdevice_notifiers(event, port->team->dev); + break; } return NOTIFY_DONE; } diff --git a/include/linux/igmp.h b/include/linux/igmp.h index e3362b5f13e8..f47550d75f85 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -129,6 +129,5 @@ extern void ip_mc_unmap(struct in_device *); extern void ip_mc_remap(struct in_device *); extern void ip_mc_dec_group(struct in_device *in_dev, __be32 addr); extern void ip_mc_inc_group(struct in_device *in_dev, __be32 addr); -extern void ip_mc_rejoin_groups(struct in_device *in_dev); #endif diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0741a1e919a5..2bb2357d83bb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1633,6 +1633,7 @@ struct packet_offload { #define NETDEV_NOTIFY_PEERS 0x0013 #define NETDEV_JOIN 0x0014 #define NETDEV_CHANGEUPPER 0x0015 +#define NETDEV_RESEND_IGMP 0x0016 extern int register_netdevice_notifier(struct notifier_block *nb); extern int unregister_netdevice_notifier(struct notifier_block *nb); diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 2fb2d88e8c2e..03a92e117657 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -459,6 +459,7 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, case NETDEV_NOTIFY_PEERS: case NETDEV_BONDING_FAILOVER: + case NETDEV_RESEND_IGMP: /* Propagate to vlan devices */ vlan_group_for_each_dev(grp, i, vlandev) call_netdevice_notifiers(event, vlandev); diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c index 3a3f371b2841..2998dd1769a0 100644 --- a/net/bridge/br_notify.c +++ b/net/bridge/br_notify.c @@ -102,6 +102,11 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v case NETDEV_PRE_TYPE_CHANGE: /* Forbid underlaying device to change its type. */ return NOTIFY_BAD; + + case NETDEV_RESEND_IGMP: + /* Propagate to master device */ + call_netdevice_notifiers(event, br->dev); + break; } /* Events that may cause spanning tree to refresh */ diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index cd71190d2962..375aca372250 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1323,16 +1323,17 @@ out: EXPORT_SYMBOL(ip_mc_inc_group); /* - * Resend IGMP JOIN report; used for bonding. - * Called with rcu_read_lock() + * Resend IGMP JOIN report; used by netdev notifier. */ -void ip_mc_rejoin_groups(struct in_device *in_dev) +static void ip_mc_rejoin_groups(struct in_device *in_dev) { #ifdef CONFIG_IP_MULTICAST struct ip_mc_list *im; int type; - for_each_pmc_rcu(in_dev, im) { + ASSERT_RTNL(); + + for_each_pmc_rtnl(in_dev, im) { if (im->multiaddr == IGMP_ALL_HOSTS) continue; @@ -1349,7 +1350,6 @@ void ip_mc_rejoin_groups(struct in_device *in_dev) } #endif } -EXPORT_SYMBOL(ip_mc_rejoin_groups); /* * A socket has left a multicast group on device dev @@ -2735,8 +2735,42 @@ static struct pernet_operations igmp_net_ops = { .exit = igmp_net_exit, }; +static int igmp_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct in_device *in_dev; + + switch (event) { + case NETDEV_RESEND_IGMP: + in_dev = __in_dev_get_rtnl(dev); + if (in_dev) + ip_mc_rejoin_groups(in_dev); + break; + default: + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block igmp_notifier = { + .notifier_call = igmp_netdev_event, +}; + int __init igmp_mc_proc_init(void) { - return register_pernet_subsys(&igmp_net_ops); + int err; + + err = register_pernet_subsys(&igmp_net_ops); + if (err) + return err; + err = register_netdevice_notifier(&igmp_notifier); + if (err) + goto reg_notif_fail; + return 0; + +reg_notif_fail: + unregister_pernet_subsys(&igmp_net_ops); + return err; } #endif -- cgit v1.2.3-71-gd317 From 492b200efdd20b8fcfdac873f3cd8d4902386581 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 20 Jul 2013 12:13:54 +0200 Subject: team: add support for sending multicast rejoins Similar to what is implemented in bonding. User is able to ask team driver to send IGMP rejoins in case port is enabled or disabled. Using previously introduced netdev notifier. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/if_team.h | 6 ++++ 2 files changed, 91 insertions(+) (limited to 'include') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 2587dc86d533..75159e4184fd 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -662,6 +662,46 @@ static void team_notify_peers_fini(struct team *team) } +/******************************* + * Send multicast group rejoins + *******************************/ + +static void team_mcast_rejoin_work(struct work_struct *work) +{ + struct team *team; + + team = container_of(work, struct team, mcast_rejoin.dw.work); + + if (!rtnl_trylock()) { + schedule_delayed_work(&team->mcast_rejoin.dw, 0); + return; + } + call_netdevice_notifiers(NETDEV_RESEND_IGMP, team->dev); + rtnl_unlock(); + if (!atomic_dec_and_test(&team->mcast_rejoin.count_pending)) + schedule_delayed_work(&team->mcast_rejoin.dw, + msecs_to_jiffies(team->mcast_rejoin.interval)); +} + +static void team_mcast_rejoin(struct team *team) +{ + if (!team->mcast_rejoin.count || !netif_running(team->dev)) + return; + atomic_set(&team->mcast_rejoin.count_pending, team->mcast_rejoin.count); + schedule_delayed_work(&team->mcast_rejoin.dw, 0); +} + +static void team_mcast_rejoin_init(struct team *team) +{ + INIT_DELAYED_WORK(&team->mcast_rejoin.dw, team_mcast_rejoin_work); +} + +static void team_mcast_rejoin_fini(struct team *team) +{ + cancel_delayed_work_sync(&team->mcast_rejoin.dw); +} + + /************************ * Rx path frame handler ************************/ @@ -887,6 +927,7 @@ static void team_port_enable(struct team *team, if (team->ops.port_enabled) team->ops.port_enabled(team, port); team_notify_peers(team); + team_mcast_rejoin(team); } static void __reconstruct_port_hlist(struct team *team, int rm_index) @@ -917,6 +958,7 @@ static void team_port_disable(struct team *team, team_queue_override_port_del(team, port); team_adjust_ops(team); team_notify_peers(team); + team_mcast_rejoin(team); } #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ @@ -1275,6 +1317,34 @@ static int team_notify_peers_interval_set(struct team *team, return 0; } +static int team_mcast_rejoin_count_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + ctx->data.u32_val = team->mcast_rejoin.count; + return 0; +} + +static int team_mcast_rejoin_count_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + team->mcast_rejoin.count = ctx->data.u32_val; + return 0; +} + +static int team_mcast_rejoin_interval_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + ctx->data.u32_val = team->mcast_rejoin.interval; + return 0; +} + +static int team_mcast_rejoin_interval_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + team->mcast_rejoin.interval = ctx->data.u32_val; + return 0; +} + static int team_port_en_option_get(struct team *team, struct team_gsetter_ctx *ctx) { @@ -1398,6 +1468,18 @@ static const struct team_option team_options[] = { .getter = team_notify_peers_interval_get, .setter = team_notify_peers_interval_set, }, + { + .name = "mcast_rejoin_count", + .type = TEAM_OPTION_TYPE_U32, + .getter = team_mcast_rejoin_count_get, + .setter = team_mcast_rejoin_count_set, + }, + { + .name = "mcast_rejoin_interval", + .type = TEAM_OPTION_TYPE_U32, + .getter = team_mcast_rejoin_interval_get, + .setter = team_mcast_rejoin_interval_set, + }, { .name = "enabled", .type = TEAM_OPTION_TYPE_BOOL, @@ -1480,6 +1562,7 @@ static int team_init(struct net_device *dev) INIT_LIST_HEAD(&team->option_inst_list); team_notify_peers_init(team); + team_mcast_rejoin_init(team); err = team_options_register(team, team_options, ARRAY_SIZE(team_options)); if (err) @@ -1491,6 +1574,7 @@ static int team_init(struct net_device *dev) return 0; err_options_register: + team_mcast_rejoin_fini(team); team_notify_peers_fini(team); team_queue_override_fini(team); err_team_queue_override_init: @@ -1511,6 +1595,7 @@ static void team_uninit(struct net_device *dev) __team_change_mode(team, NULL); /* cleanup */ __team_options_unregister(team, team_options, ARRAY_SIZE(team_options)); + team_mcast_rejoin_fini(team); team_notify_peers_fini(team); team_queue_override_fini(team); mutex_unlock(&team->lock); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index b0b83683461e..a899dc24be15 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -200,6 +200,12 @@ struct team { atomic_t count_pending; struct delayed_work dw; } notify_peers; + struct { + unsigned int count; + unsigned int interval; /* in ms */ + atomic_t count_pending; + struct delayed_work dw; + } mcast_rejoin; long mode_priv[TEAM_MODE_PRIV_LONGS]; }; -- cgit v1.2.3-71-gd317 From 40d5e0905a03601d40cd4e46b8690093c2355d03 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 15 Jun 2013 10:21:17 -0400 Subject: n_tty: Fix EOF push handling In canonical mode, an EOF which is not the first character of the line causes read() to complete and return the number of characters read so far (commonly referred to as EOF push). However, if the previous read() returned because the user buffer was full _and_ the next character is an EOF not at the beginning of the line, read() must not return 0, thus mistakenly indicating the end-of-file condition. The TTY_PUSH flag is used to indicate an EOF was received which is not at the beginning of the line. Because the EOF push condition is evaluated by a thread other than the read(), multiple EOF pushes can cause a premature end-of-file to be indicated. Instead, discover the 'EOF push as first read character' condition from the read() thread itself, and restart the i/o loop if detected. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 34 +++++++++++++++++----------------- drivers/tty/tty_io.c | 1 - include/linux/tty.h | 1 - 3 files changed, 17 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index d4d71350a71d..25aaf1afa221 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -113,6 +113,7 @@ struct n_tty_data { /* consumer-published */ size_t read_tail; + size_t line_start; /* protected by output lock */ unsigned int column; @@ -337,6 +338,7 @@ static void reset_buffer_flags(struct n_tty_data *ldata) { ldata->read_head = ldata->canon_head = ldata->read_tail = 0; ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0; + ldata->line_start = 0; ldata->erasing = 0; bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); @@ -1396,8 +1398,6 @@ send_signal: if (c == EOF_CHAR(tty)) { if (read_cnt(ldata) >= N_TTY_BUF_SIZE) return; - if (ldata->canon_head != ldata->read_head) - set_bit(TTY_PUSH, &tty->flags); c = __DISABLED_CHAR; goto handle_newline; } @@ -1604,6 +1604,7 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) canon_change = (old->c_lflag ^ tty->termios.c_lflag) & ICANON; if (canon_change) { bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); + ldata->line_start = 0; ldata->canon_head = ldata->read_tail; ldata->erasing = 0; ldata->lnext = 0; @@ -1837,6 +1838,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, size_t eol; size_t tail; int ret, found = 0; + bool eof_push = 0; /* N.B. avoid overrun if nr == 0 */ n = min(*nr, read_cnt(ldata)); @@ -1863,8 +1865,10 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, n = (found + eol + size) & (N_TTY_BUF_SIZE - 1); c = n; - if (found && read_buf(ldata, eol) == __DISABLED_CHAR) + if (found && read_buf(ldata, eol) == __DISABLED_CHAR) { n--; + eof_push = !n && ldata->read_tail != ldata->line_start; + } n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu size:%zu more:%zu\n", __func__, eol, found, n, c, size, more); @@ -1887,9 +1891,11 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, smp_mb__after_clear_bit(); ldata->read_tail += c; - if (found) + if (found) { + ldata->line_start = ldata->read_tail; tty_audit_push(tty); - return 0; + } + return eof_push ? -EAGAIN : 0; } extern ssize_t redirected_tty_write(struct file *, const char __user *, @@ -1964,12 +1970,10 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, int c; int minimum, time; ssize_t retval = 0; - ssize_t size; long timeout; unsigned long flags; int packet; -do_it_again: c = job_control(tty, file); if (c < 0) return c; @@ -2076,7 +2080,10 @@ do_it_again: if (ldata->icanon && !L_EXTPROC(tty)) { retval = canon_copy_from_read_buf(tty, &b, &nr); - if (retval) + if (retval == -EAGAIN) { + retval = 0; + continue; + } else if (retval) break; } else { int uncopied; @@ -2104,15 +2111,8 @@ do_it_again: ldata->minimum_to_wake = minimum; __set_current_state(TASK_RUNNING); - size = b - buf; - if (size) { - retval = size; - if (nr) - clear_bit(TTY_PUSH, &tty->flags); - } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) { - up_read(&tty->termios_rwsem); - goto do_it_again; - } + if (b - buf) + retval = b - buf; n_tty_set_room(tty); up_read(&tty->termios_rwsem); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 1b32da6f6cdd..2174698dd6f7 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -664,7 +664,6 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session) spin_lock_irq(&tty->ctrl_lock); clear_bit(TTY_THROTTLED, &tty->flags); - clear_bit(TTY_PUSH, &tty->flags); clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); put_pid(tty->session); put_pid(tty->pgrp); diff --git a/include/linux/tty.h b/include/linux/tty.h index 5fd5d6f1ebc4..554b732d8b55 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -304,7 +304,6 @@ struct tty_file_private { #define TTY_EXCLUSIVE 3 /* Exclusive open mode */ #define TTY_DEBUG 4 /* Debugging */ #define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */ -#define TTY_PUSH 6 /* n_tty private */ #define TTY_CLOSING 7 /* ->close() in progress */ #define TTY_LDISC_OPEN 11 /* Line discipline is open */ #define TTY_PTY_LOCK 16 /* pty private */ -- cgit v1.2.3-71-gd317 From 2adfffa223500b739b17dcf3d710cec344f7608f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 17 Jun 2013 16:48:13 +0200 Subject: OF: make of_property_for_each_{u32|string}() use parameters if OF is not enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I am getting a few |warning: unused variable ‘p’ [-Wunused-variable] |warning: unused variable ‘prop’ [-Wunused-variable] in the case where CONFIG_OF is not defined and the parameters are only used in the loop macro. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Grant Likely --- include/linux/of.h | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/of.h b/include/linux/of.h index 1fd08ca23106..90a8811e9e48 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -323,12 +323,6 @@ extern int of_detach_node(struct device_node *); */ const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur, u32 *pu); -#define of_property_for_each_u32(np, propname, prop, p, u) \ - for (prop = of_find_property(np, propname, NULL), \ - p = of_prop_next_u32(prop, NULL, &u); \ - p; \ - p = of_prop_next_u32(prop, p, &u)) - /* * struct property *prop; * const char *s; @@ -337,11 +331,6 @@ const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur, * printk("String value: %s\n", s); */ const char *of_prop_next_string(struct property *prop, const char *cur); -#define of_property_for_each_string(np, propname, prop, s) \ - for (prop = of_find_property(np, propname, NULL), \ - s = of_prop_next_string(prop, NULL); \ - s; \ - s = of_prop_next_string(prop, s)) #else /* CONFIG_OF */ @@ -505,12 +494,20 @@ static inline int of_machine_is_compatible(const char *compat) return 0; } +static inline const __be32 *of_prop_next_u32(struct property *prop, + const __be32 *cur, u32 *pu) +{ + return NULL; +} + +static inline const char *of_prop_next_string(struct property *prop, + const char *cur) +{ + return NULL; +} + #define of_match_ptr(_ptr) NULL #define of_match_node(_matches, _node) NULL -#define of_property_for_each_u32(np, propname, prop, p, u) \ - while (0) -#define of_property_for_each_string(np, propname, prop, s) \ - while (0) #endif /* CONFIG_OF */ #ifndef of_node_to_nid @@ -559,6 +556,18 @@ static inline int of_property_read_u32(const struct device_node *np, return of_property_read_u32_array(np, propname, out_value, 1); } +#define of_property_for_each_u32(np, propname, prop, p, u) \ + for (prop = of_find_property(np, propname, NULL), \ + p = of_prop_next_u32(prop, NULL, &u); \ + p; \ + p = of_prop_next_u32(prop, p, &u)) + +#define of_property_for_each_string(np, propname, prop, s) \ + for (prop = of_find_property(np, propname, NULL), \ + s = of_prop_next_string(prop, NULL); \ + s; \ + s = of_prop_next_string(prop, s)) + #if defined(CONFIG_PROC_FS) && defined(CONFIG_PROC_DEVICETREE) extern void proc_device_tree_add_node(struct device_node *, struct proc_dir_entry *); extern void proc_device_tree_add_prop(struct proc_dir_entry *pde, struct property *prop); -- cgit v1.2.3-71-gd317 From 374d5c9964c10373ba39bbe934f4262eb87d7114 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Mon, 1 Jul 2013 14:20:35 -0400 Subject: of: Specify initrd location using 64-bit On some PAE architectures, the entire range of physical memory could reside outside the 32-bit limit. These systems need the ability to specify the initrd location using 64-bit numbers. This patch globally modifies the early_init_dt_setup_initrd_arch() function to use 64-bit numbers instead of the current unsigned long. There has been quite a bit of debate about whether to use u64 or phys_addr_t. It was concluded to stick to u64 to be consistent with rest of the device tree code. As summarized by Geert, "The address to load the initrd is decided by the bootloader/user and set at that point later in time. The dtb should not be tied to the kernel you are booting" More details on the discussion can be found here: https://lkml.org/lkml/2013/6/20/690 https://lkml.org/lkml/2012/9/13/544 Signed-off-by: Santosh Shilimkar Acked-by: Rob Herring Acked-by: Vineet Gupta Acked-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Grant Likely --- arch/arc/mm/init.c | 5 ++--- arch/arm/mm/init.c | 2 +- arch/arm64/mm/init.c | 3 +-- arch/c6x/kernel/devicetree.c | 3 +-- arch/metag/mm/init.c | 5 ++--- arch/microblaze/kernel/prom.c | 3 +-- arch/mips/kernel/prom.c | 3 +-- arch/openrisc/kernel/prom.c | 3 +-- arch/powerpc/kernel/prom.c | 3 +-- arch/x86/kernel/devicetree.c | 3 +-- arch/xtensa/kernel/setup.c | 3 +-- drivers/of/fdt.c | 10 ++++++---- include/linux/of_fdt.h | 3 +-- 13 files changed, 20 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/arch/arc/mm/init.c b/arch/arc/mm/init.c index a08ce7185423..81279ec73a6a 100644 --- a/arch/arc/mm/init.c +++ b/arch/arc/mm/init.c @@ -127,9 +127,8 @@ void __init free_initrd_mem(unsigned long start, unsigned long end) #endif #ifdef CONFIG_OF_FLATTREE -void __init early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end) +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { - pr_err("%s(%lx, %lx)\n", __func__, start, end); + pr_err("%s(%llx, %llx)\n", __func__, start, end); } #endif /* CONFIG_OF_FLATTREE */ diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 15225d829d71..81484177c9b8 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -77,7 +77,7 @@ static int __init parse_tag_initrd2(const struct tag *tag) __tagtable(ATAG_INITRD2, parse_tag_initrd2); #ifdef CONFIG_OF_FLATTREE -void __init early_init_dt_setup_initrd_arch(unsigned long start, unsigned long end) +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { phys_initrd_start = start; phys_initrd_size = end - start; diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index 67e8d7ce3fe7..de2de5db628d 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -44,8 +44,7 @@ static unsigned long phys_initrd_size __initdata = 0; phys_addr_t memstart_addr __read_mostly = 0; -void __init early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end) +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { phys_initrd_start = start; phys_initrd_size = end - start; diff --git a/arch/c6x/kernel/devicetree.c b/arch/c6x/kernel/devicetree.c index bdb56f09d0ac..287d0e64dfba 100644 --- a/arch/c6x/kernel/devicetree.c +++ b/arch/c6x/kernel/devicetree.c @@ -33,8 +33,7 @@ void __init early_init_devtree(void *params) #ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end) +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { initrd_start = (unsigned long)__va(start); initrd_end = (unsigned long)__va(end); diff --git a/arch/metag/mm/init.c b/arch/metag/mm/init.c index 28813f164730..123919534b80 100644 --- a/arch/metag/mm/init.c +++ b/arch/metag/mm/init.c @@ -407,10 +407,9 @@ void free_initrd_mem(unsigned long start, unsigned long end) #endif #ifdef CONFIG_OF_FLATTREE -void __init early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end) +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { - pr_err("%s(%lx, %lx)\n", + pr_err("%s(%llx, %llx)\n", __func__, start, end); } #endif /* CONFIG_OF_FLATTREE */ diff --git a/arch/microblaze/kernel/prom.c b/arch/microblaze/kernel/prom.c index 0a2c68f9f9b0..62e2e8f2c5d6 100644 --- a/arch/microblaze/kernel/prom.c +++ b/arch/microblaze/kernel/prom.c @@ -136,8 +136,7 @@ void __init early_init_devtree(void *params) } #ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end) +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { initrd_start = (unsigned long)__va(start); initrd_end = (unsigned long)__va(end); diff --git a/arch/mips/kernel/prom.c b/arch/mips/kernel/prom.c index 7e954042f252..0fa0b69cdd53 100644 --- a/arch/mips/kernel/prom.c +++ b/arch/mips/kernel/prom.c @@ -58,8 +58,7 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) } #ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end) +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { initrd_start = (unsigned long)__va(start); initrd_end = (unsigned long)__va(end); diff --git a/arch/openrisc/kernel/prom.c b/arch/openrisc/kernel/prom.c index 5869e3fa5dd3..150215a91711 100644 --- a/arch/openrisc/kernel/prom.c +++ b/arch/openrisc/kernel/prom.c @@ -96,8 +96,7 @@ void __init early_init_devtree(void *params) } #ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end) +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { initrd_start = (unsigned long)__va(start); initrd_end = (unsigned long)__va(end); diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index eb23ac92abb9..67d18dad4368 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -550,8 +550,7 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) } #ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end) +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { initrd_start = (unsigned long)__va(start); initrd_end = (unsigned long)__va(end); diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c index 69eb2fa25494..376dc7873447 100644 --- a/arch/x86/kernel/devicetree.c +++ b/arch/x86/kernel/devicetree.c @@ -52,8 +52,7 @@ void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align) } #ifdef CONFIG_BLK_DEV_INITRD -void __init early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end) +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { initrd_start = (unsigned long)__va(start); initrd_end = (unsigned long)__va(end); diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index 42a8bba0b0ea..101012bc1ff6 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -170,8 +170,7 @@ static int __init parse_tag_fdt(const bp_tag_t *tag) __tagtable(BP_TAG_FDT, parse_tag_fdt); -void __init early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end) +void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) { initrd_start = (void *)__va(start); initrd_end = (void *)__va(end); diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 6bb7cf2de556..3f473d158d79 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -550,7 +550,8 @@ int __init of_flat_dt_match(unsigned long node, const char *const *compat) */ void __init early_init_dt_check_for_initrd(unsigned long node) { - unsigned long start, end, len; + u64 start, end; + unsigned long len; __be32 *prop; pr_debug("Looking for initrd properties... "); @@ -558,15 +559,16 @@ void __init early_init_dt_check_for_initrd(unsigned long node) prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len); if (!prop) return; - start = of_read_ulong(prop, len/4); + start = of_read_number(prop, len/4); prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len); if (!prop) return; - end = of_read_ulong(prop, len/4); + end = of_read_number(prop, len/4); early_init_dt_setup_initrd_arch(start, end); - pr_debug("initrd_start=0x%lx initrd_end=0x%lx\n", start, end); + pr_debug("initrd_start=0x%llx initrd_end=0x%llx\n", + (unsigned long long)start, (unsigned long long)end); } #else inline void early_init_dt_check_for_initrd(unsigned long node) diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index ed136ad698ce..4a17939b95cc 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -106,8 +106,7 @@ extern u64 dt_mem_next_cell(int s, __be32 **cellp); * physical addresses. */ #ifdef CONFIG_BLK_DEV_INITRD -extern void early_init_dt_setup_initrd_arch(unsigned long start, - unsigned long end); +extern void early_init_dt_setup_initrd_arch(u64 start, u64 end); #endif /* Early flat tree scan hooks */ -- cgit v1.2.3-71-gd317 From c3f48ae6fd5a1ebdcaff5efe35f88f31daaee225 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jul 2013 15:27:36 +0200 Subject: ASoC: dapm: Pass snd_soc_card directly to soc_dpcm_runtime_update() soc_dpcm_runtime_update() operates on a ASoC card as a whole. Currently it takes a snd_soc_dapm_widget as its only parameter though. The widget is then used to look up the card and is otherwise unused. This patch changes the function to take a pointer to the card directly. This makes it possible to to call soc_dpcm_runtime_update() for updates which are not related to one specific widget. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dpcm.h | 2 +- sound/soc/soc-dapm.c | 4 ++-- sound/soc/soc-pcm.c | 10 +--------- 3 files changed, 4 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 04598f1efd77..047d657c331c 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -133,6 +133,6 @@ void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream, /* internal use only */ int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute); int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd); -int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *); +int soc_dpcm_runtime_update(struct snd_soc_card *); #endif diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 378655839f74..8d9c09b266fd 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1986,7 +1986,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e); mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(widget); + soc_dpcm_runtime_update(card); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); @@ -2032,7 +2032,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, ret = soc_dapm_mixer_update_power(widget, kcontrol, connect); mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(widget); + soc_dpcm_runtime_update(card); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b6c640332a17..5c2c66209808 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1832,18 +1832,10 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream) /* Called by DAPM mixer/mux changes to update audio routing between PCMs and * any DAI links. */ -int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *widget) +int soc_dpcm_runtime_update(struct snd_soc_card *card) { - struct snd_soc_card *card; int i, old, new, paths; - if (widget->codec) - card = widget->codec->card; - else if (widget->platform) - card = widget->platform->card; - else - return -EINVAL; - mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); for (i = 0; i < card->num_rtd; i++) { struct snd_soc_dapm_widget_list *list; -- cgit v1.2.3-71-gd317 From ce6cfaf1de136cd3e6ed7c0ed984be8d003a58c1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jul 2013 15:27:37 +0200 Subject: ASoC: dapm: Run widget updates for shared controls at the same time Currently when updating a control that is shared between multiple widgets the whole power-up/power-down sequence is being run once for each widget. The control register is updated during the first run, which means the CODEC internal routing is also updated for all widgets during this first run. The input and output paths for each widgets are only updated though during the respective run for that widget. This leads to a slight inconsistency between the CODEC's internal state and ASoC's state, which causes non optimal behavior in regard to click and pop avoidance. E.g. consider the following setup where two MUXs share the same control. +------+ A1 ------| | | MUX1 |----- C1 B1 ------| | +------+ | control ---+ | +------+ A2 ------| | | MUX2 |----- C2 B2 ------| | +------+ If the control is updated to switch the MUXs from input A to input B with the current code the power-up/power-down sequence will look like this: Run soc_dapm_mux_update_power for MUX1 Power-down A1 Update MUXing Power-up B1 Run soc_dapm_mux_update_power for MUX2 Power-down A2 (Update MUXing) Power-up B2 Note that the second 'Update Muxing' is a no-op, since the register was already updated. While the preferred order for avoiding pops and clicks should be: Run soc_dapm_mux_update_power for control Power-down A1 Power-down A2 Update MUXing Power-up B1 Power-up B2 This patch changes the behavior to the later by running the updates for all widgets that the control is attached to at the same time. The new code is also a bit simpler since callers of soc_dapm_{mux,muxer}_update_power don't have to loop over each widget anymore and neither do we need to keep track for which of the kcontrol's widgets the current update is. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 7 +- sound/soc/soc-dapm.c | 162 ++++++++++++++++++++--------------------------- 2 files changed, 71 insertions(+), 98 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3e479f4e15f5..3717ad089486 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -391,10 +391,10 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream, void snd_soc_dapm_shutdown(struct snd_soc_card *card); /* external DAPM widget events */ -int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, +int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, struct snd_kcontrol *kcontrol, int connect); -int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e); +int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e); /* dapm sys fs - used by the core */ int snd_soc_dapm_sys_add(struct device *dev); @@ -559,7 +559,6 @@ struct snd_soc_dapm_widget { }; struct snd_soc_dapm_update { - struct snd_soc_dapm_widget *widget; struct snd_kcontrol *kcontrol; int reg; int mask; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8d9c09b266fd..6f8a01bf6ca8 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1456,34 +1456,45 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, static void dapm_widget_update(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_update *update = dapm->update; - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget_list *wlist; + struct snd_soc_dapm_widget *w = NULL; + unsigned int wi; int ret; if (!update) return; - w = update->widget; + wlist = snd_kcontrol_chip(update->kcontrol); - if (w->event && - (w->event_flags & SND_SOC_DAPM_PRE_REG)) { - ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); - if (ret != 0) - dev_err(dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n", - w->name, ret); + for (wi = 0; wi < wlist->num_widgets; wi++) { + w = wlist->widgets[wi]; + + if (w->event && (w->event_flags & SND_SOC_DAPM_PRE_REG)) { + ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); + if (ret != 0) + dev_err(dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n", + w->name, ret); + } } + if (!w) + return; + ret = soc_widget_update_bits_locked(w, update->reg, update->mask, update->val); if (ret < 0) dev_err(dapm->dev, "ASoC: %s DAPM update failed: %d\n", w->name, ret); - if (w->event && - (w->event_flags & SND_SOC_DAPM_POST_REG)) { - ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); - if (ret != 0) - dev_err(dapm->dev, "ASoC: %s DAPM post-event failed: %d\n", - w->name, ret); + for (wi = 0; wi < wlist->num_widgets; wi++) { + w = wlist->widgets[wi]; + + if (w->event && (w->event_flags & SND_SOC_DAPM_POST_REG)) { + ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); + if (ret != 0) + dev_err(dapm->dev, "ASoC: %s DAPM post-event failed: %d\n", + w->name, ret); + } } } @@ -1936,19 +1947,14 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) #endif /* test and update the power status of a mux widget */ -static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, +static int soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) { struct snd_soc_dapm_path *path; int found = 0; - if (widget->id != snd_soc_dapm_mux && - widget->id != snd_soc_dapm_virt_mux && - widget->id != snd_soc_dapm_value_mux) - return -ENODEV; - /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->dapm->card->paths, list) { + list_for_each_entry(path, &dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue; @@ -1966,24 +1972,23 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, "mux disconnection"); path->connect = 0; /* old connection must be powered down */ } + dapm_mark_dirty(path->sink, "mux change"); } - if (found) { - dapm_mark_dirty(widget, "mux change"); - dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); - } + if (found) + dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); return found; } -int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) +int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) { - struct snd_soc_card *card = widget->dapm->card; + struct snd_soc_card *card = dapm->card; int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e); + ret = soc_dapm_mux_update_power(dapm, kcontrol, mux, e); mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -1992,19 +1997,14 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget, EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); /* test and update the power status of a mixer or switch widget */ -static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, +static int soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, struct snd_kcontrol *kcontrol, int connect) { struct snd_soc_dapm_path *path; int found = 0; - if (widget->id != snd_soc_dapm_mixer && - widget->id != snd_soc_dapm_mixer_named_ctl && - widget->id != snd_soc_dapm_switch) - return -ENODEV; - /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &widget->dapm->card->paths, list) { + list_for_each_entry(path, &dapm->card->paths, list) { if (path->kcontrol != kcontrol) continue; @@ -2012,24 +2012,23 @@ static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, found = 1; path->connect = connect; dapm_mark_dirty(path->source, "mixer connection"); + dapm_mark_dirty(path->sink, "mixer update"); } - if (found) { - dapm_mark_dirty(widget, "mixer update"); - dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); - } + if (found) + dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); return found; } -int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, - struct snd_kcontrol *kcontrol, int connect) +int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, + struct snd_kcontrol *kcontrol, int connect) { - struct snd_soc_card *card = widget->dapm->card; + struct snd_soc_card *card = dapm->card; int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - ret = soc_dapm_mixer_update_power(widget, kcontrol, connect); + ret = soc_dapm_mixer_update_power(dapm, kcontrol, connect); mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2695,7 +2694,6 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, unsigned int val; int connect, change; struct snd_soc_dapm_update update; - int wi; if (snd_soc_volsw_is_stereo(mc)) dev_warn(widget->dapm->dev, @@ -2714,22 +2712,16 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, change = snd_soc_test_bits(widget->codec, reg, mask, val); if (change) { - for (wi = 0; wi < wlist->num_widgets; wi++) { - widget = wlist->widgets[wi]; - - widget->value = val; + update.kcontrol = kcontrol; + update.reg = reg; + update.mask = mask; + update.val = val; - update.kcontrol = kcontrol; - update.widget = widget; - update.reg = reg; - update.mask = mask; - update.val = val; - widget->dapm->update = &update; + widget->dapm->update = &update; - soc_dapm_mixer_update_power(widget, kcontrol, connect); + soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect); - widget->dapm->update = NULL; - } + widget->dapm->update = NULL; } mutex_unlock(&card->dapm_mutex); @@ -2784,7 +2776,6 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, unsigned int val, mux, change; unsigned int mask; struct snd_soc_dapm_update update; - int wi; if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -2802,22 +2793,17 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, change = snd_soc_test_bits(widget->codec, e->reg, mask, val); if (change) { - for (wi = 0; wi < wlist->num_widgets; wi++) { - widget = wlist->widgets[wi]; + widget->value = val; - widget->value = val; + update.kcontrol = kcontrol; + update.reg = e->reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; - update.kcontrol = kcontrol; - update.widget = widget; - update.reg = e->reg; - update.mask = mask; - update.val = val; - widget->dapm->update = &update; + soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); - soc_dapm_mux_update_power(widget, kcontrol, mux, e); - - widget->dapm->update = NULL; - } + widget->dapm->update = NULL; } mutex_unlock(&card->dapm_mutex); @@ -2861,7 +2847,6 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; - int wi; if (ucontrol->value.enumerated.item[0] >= e->max) return -EINVAL; @@ -2870,13 +2855,8 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, change = widget->value != ucontrol->value.enumerated.item[0]; if (change) { - for (wi = 0; wi < wlist->num_widgets; wi++) { - widget = wlist->widgets[wi]; - - widget->value = ucontrol->value.enumerated.item[0]; - - soc_dapm_mux_update_power(widget, kcontrol, widget->value, e); - } + widget->value = ucontrol->value.enumerated.item[0]; + soc_dapm_mux_update_power(widget->dapm, kcontrol, widget->value, e); } mutex_unlock(&card->dapm_mutex); @@ -2949,7 +2929,6 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, unsigned int val, mux, change; unsigned int mask; struct snd_soc_dapm_update update; - int wi; if (ucontrol->value.enumerated.item[0] > e->max - 1) return -EINVAL; @@ -2967,22 +2946,17 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, change = snd_soc_test_bits(widget->codec, e->reg, mask, val); if (change) { - for (wi = 0; wi < wlist->num_widgets; wi++) { - widget = wlist->widgets[wi]; - - widget->value = val; + widget->value = val; - update.kcontrol = kcontrol; - update.widget = widget; - update.reg = e->reg; - update.mask = mask; - update.val = val; - widget->dapm->update = &update; + update.kcontrol = kcontrol; + update.reg = e->reg; + update.mask = mask; + update.val = val; + widget->dapm->update = &update; - soc_dapm_mux_update_power(widget, kcontrol, mux, e); + soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); - widget->dapm->update = NULL; - } + widget->dapm->update = NULL; } mutex_unlock(&card->dapm_mutex); -- cgit v1.2.3-71-gd317 From 6b3fc03b3b614ced09df96ca60ab6f627d8c240c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 24 Jul 2013 15:27:38 +0200 Subject: ASoC: dapm: Add a update parameter to snd_soc_dapm_{mux,mixer}_update_power In order to avoid race conditions the assignment of dapm->update should happen while card->dapm_mutex is being held. To allow CODEC drivers to run a register update when using snd_soc_dapm_mux_update_power() or snd_soc_dapm_mixer_update_power() add a update parameter to these two functions. The update parameter will be assigned to dapm->update while card->dapm_mutex is locked. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 7 +++++-- sound/soc/soc-dapm.c | 10 ++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3717ad089486..e77c6f5a8390 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -333,6 +333,7 @@ struct snd_soc_dapm_route; struct snd_soc_dapm_context; struct regulator; struct snd_soc_dapm_widget_list; +struct snd_soc_dapm_update; int dapm_reg_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -392,9 +393,11 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card); /* external DAPM widget events */ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int connect); + struct snd_kcontrol *kcontrol, int connect, + struct snd_soc_dapm_update *update); int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e); + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e, + struct snd_soc_dapm_update *update); /* dapm sys fs - used by the core */ int snd_soc_dapm_sys_add(struct device *dev); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 6f8a01bf6ca8..758761146a42 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1982,13 +1982,16 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, } int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e) + struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e, + struct snd_soc_dapm_update *update) { struct snd_soc_card *card = dapm->card; int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + dapm->update = update; ret = soc_dapm_mux_update_power(dapm, kcontrol, mux, e); + dapm->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2022,13 +2025,16 @@ static int soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, } int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, - struct snd_kcontrol *kcontrol, int connect) + struct snd_kcontrol *kcontrol, int connect, + struct snd_soc_dapm_update *update) { struct snd_soc_card *card = dapm->card; int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + dapm->update = update; ret = soc_dapm_mixer_update_power(dapm, kcontrol, connect); + dapm->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); -- cgit v1.2.3-71-gd317 From f484f7a6bc6f0f871627cc1f83f56077a5dc6e47 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 24 Jul 2013 10:26:42 +0200 Subject: regmap: irq: make flags bool and put them in a bitfield This patch makes mask/wake_invert bool and puts all flags into a bitfield for consistency and to save some space. Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown --- include/linux/regmap.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 34ebe7778033..99b0f7cad75c 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -486,10 +486,10 @@ struct regmap_irq_chip { unsigned int ack_base; unsigned int wake_base; unsigned int irq_reg_stride; - bool init_ack_masked; - unsigned int mask_invert; - unsigned int wake_invert; - bool runtime_pm; + bool init_ack_masked:1; + bool mask_invert:1; + bool wake_invert:1; + bool runtime_pm:1; int num_regs; -- cgit v1.2.3-71-gd317 From 68622bdfefb969fd7cf710545d7e758a732ab01d Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 24 Jul 2013 10:26:48 +0200 Subject: regmap: irq: document mask/wake_invert flags Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown --- include/linux/regmap.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 99b0f7cad75c..a3c68aad2381 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -471,6 +471,8 @@ struct regmap_irq { * @wake_base: Base address for wake enables. If zero unsupported. * @irq_reg_stride: Stride to use for chips where registers are not contiguous. * @init_ack_masked: Ack all masked interrupts once during initalization. + * @mask_invert: Inverted mask register: cleared bits are masked out. + * @wake_invert: Inverted wake register: cleared bits are wake enabled. * @runtime_pm: Hold a runtime PM lock on the device when accessing it. * * @num_regs: Number of registers in each control bank. -- cgit v1.2.3-71-gd317 From 5ff9d8a65ce80efb509ce4e8051394e9ed2cd942 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 29 Mar 2013 21:04:39 -0700 Subject: vfs: Lock in place mounts from more privileged users When creating a less privileged mount namespace or propogating mounts from a more privileged to a less privileged mount namespace lock the submounts so they may not be unmounted individually in the child mount namespace revealing what is under them. This enforces the reasonable expectation that it is not possible to see under a mount point. Most of the time mounts are on empty directories and revealing that does not matter, however I have seen an occassionaly sloppy configuration where there were interesting things concealed under a mount point that probably should not be revealed. Expirable submounts are not locked because they will eventually unmount automatically so whatever is under them already needs to be safe for unprivileged users to access. From a practical standpoint these restrictions do not appear to be significant for unprivileged users of the mount namespace. Recursive bind mounts and pivot_root continues to work, and mounts that are created in a mount namespace may be unmounted there. All of which means that the common idiom of keeping a directory of interesting files and using pivot_root to throw everything else away continues to work just fine. Acked-by: Serge Hallyn Acked-by: Andy Lutomirski Signed-off-by: "Eric W. Biederman" --- fs/namespace.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/mount.h | 1 + 2 files changed, 35 insertions(+) (limited to 'include') diff --git a/fs/namespace.c b/fs/namespace.c index 7b1ca9ba0b0a..7e16a730559c 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -831,6 +831,10 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY)) mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; + /* Don't allow unprivileged users to reveal what is under a mount */ + if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire)) + mnt->mnt.mnt_flags |= MNT_LOCKED; + atomic_inc(&sb->s_active); mnt->mnt.mnt_sb = sb; mnt->mnt.mnt_root = dget(root); @@ -1327,6 +1331,8 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags) goto dput_and_out; if (!check_mnt(mnt)) goto dput_and_out; + if (mnt->mnt.mnt_flags & MNT_LOCKED) + goto dput_and_out; retval = do_umount(mnt, flags); dput_and_out: @@ -1381,6 +1387,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry, if (IS_ERR(q)) return q; + q->mnt.mnt_flags &= ~MNT_LOCKED; q->mnt_mountpoint = mnt->mnt_mountpoint; p = mnt; @@ -1696,6 +1703,19 @@ static int do_change_type(struct path *path, int flag) return err; } +static bool has_locked_children(struct mount *mnt, struct dentry *dentry) +{ + struct mount *child; + list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) { + if (!is_subdir(child->mnt_mountpoint, dentry)) + continue; + + if (child->mnt.mnt_flags & MNT_LOCKED) + return true; + } + return false; +} + /* * do loopback mount. */ @@ -1731,6 +1751,9 @@ static int do_loopback(struct path *path, const char *old_name, if (!check_mnt(parent) || !check_mnt(old)) goto out2; + if (!recurse && has_locked_children(old, old_path.dentry)) + goto out2; + if (recurse) mnt = copy_tree(old, old_path.dentry, 0); else @@ -1741,6 +1764,8 @@ static int do_loopback(struct path *path, const char *old_name, goto out2; } + mnt->mnt.mnt_flags &= ~MNT_LOCKED; + err = graft_tree(mnt, parent, mp); if (err) { br_write_lock(&vfsmount_lock); @@ -1853,6 +1878,9 @@ static int do_move_mount(struct path *path, const char *old_name) if (!check_mnt(p) || !check_mnt(old)) goto out1; + if (old->mnt.mnt_flags & MNT_LOCKED) + goto out1; + err = -EINVAL; if (old_path.dentry != old_path.mnt->mnt_root) goto out1; @@ -2630,6 +2658,8 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, goto out4; if (!check_mnt(root_mnt) || !check_mnt(new_mnt)) goto out4; + if (new_mnt->mnt.mnt_flags & MNT_LOCKED) + goto out4; error = -ENOENT; if (d_unlinked(new.dentry)) goto out4; @@ -2653,6 +2683,10 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, br_write_lock(&vfsmount_lock); detach_mnt(new_mnt, &parent_path); detach_mnt(root_mnt, &root_parent); + if (root_mnt->mnt.mnt_flags & MNT_LOCKED) { + new_mnt->mnt.mnt_flags |= MNT_LOCKED; + root_mnt->mnt.mnt_flags &= ~MNT_LOCKED; + } /* mount old root on put_old */ attach_mnt(root_mnt, old_mnt, old_mp); /* mount new_root on / */ diff --git a/include/linux/mount.h b/include/linux/mount.h index 73005f9957ea..38cd98f112a0 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -48,6 +48,7 @@ struct mnt_namespace; #define MNT_INTERNAL 0x4000 #define MNT_LOCK_READONLY 0x400000 +#define MNT_LOCKED 0x800000 struct vfsmount { struct dentry *mnt_root; /* root of the mounted tree */ -- cgit v1.2.3-71-gd317 From dee4a0be69c0e2996188e0c46478eadc280a8954 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 24 Jul 2013 16:43:51 -0400 Subject: tty: Fix lock order in tty_do_resize() Commits 6a1c0680cf3ba94356ecd58833e1540c93472a57 and 9356b535fcb71db494fc434acceb79f56d15bda2, respectively 'tty: Convert termios_mutex to termios_rwsem' and 'n_tty: Access termios values safely' introduced a circular lock dependency with console_lock and termios_rwsem. The lockdep report [1] shows that n_tty_write() will attempt to claim console_lock while holding the termios_rwsem, whereas tty_do_resize() may already hold the console_lock while claiming the termios_rwsem. Since n_tty_write() and tty_do_resize() do not contend over the same data -- the tty->winsize structure -- correct the lock dependency by introducing a new lock which specifically serializes access to tty->winsize only. [1] Lockdep report ====================================================== [ INFO: possible circular locking dependency detected ] 3.10.0-0+tip-xeon+lockdep #0+tip Not tainted ------------------------------------------------------- modprobe/277 is trying to acquire lock: (&tty->termios_rwsem){++++..}, at: [] tty_do_resize+0x36/0xe0 but task is already holding lock: ((fb_notifier_list).rwsem){.+.+.+}, at: [] __blocking_notifier_call_chain+0x56/0xc0 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #2 ((fb_notifier_list).rwsem){.+.+.+}: [] lock_acquire+0x92/0x1f0 [] down_read+0x47/0x5c [] __blocking_notifier_call_chain+0x56/0xc0 [] blocking_notifier_call_chain+0x16/0x20 [] fb_notifier_call_chain+0x1b/0x20 [] register_framebuffer+0x1e2/0x320 [] drm_fb_helper_initial_config+0x371/0x540 [drm_kms_helper] [] nouveau_fbcon_init+0x105/0x140 [nouveau] [] nouveau_drm_load+0x43f/0x610 [nouveau] [] drm_get_pci_dev+0x17e/0x2a0 [drm] [] nouveau_drm_probe+0x25a/0x2a0 [nouveau] [] local_pci_probe+0x4b/0x80 [] pci_device_probe+0x111/0x120 [] driver_probe_device+0x8b/0x3a0 [] __driver_attach+0xab/0xb0 [] bus_for_each_dev+0x5d/0xa0 [] driver_attach+0x1e/0x20 [] bus_add_driver+0x111/0x290 [] driver_register+0x77/0x170 [] __pci_register_driver+0x64/0x70 [] drm_pci_init+0x11a/0x130 [drm] [] nouveau_drm_init+0x4d/0x1000 [nouveau] [] do_one_initcall+0xea/0x1a0 [] load_module+0x123b/0x1bf0 [] SyS_init_module+0xd7/0x120 [] system_call_fastpath+0x16/0x1b -> #1 (console_lock){+.+.+.}: [] lock_acquire+0x92/0x1f0 [] console_lock+0x77/0x80 [] con_flush_chars+0x31/0x50 [] n_tty_write+0x1ec/0x4d0 [] tty_write+0x159/0x2e0 [] redirected_tty_write+0xb5/0xc0 [] vfs_write+0xc5/0x1f0 [] SyS_write+0x55/0xa0 [] system_call_fastpath+0x16/0x1b -> #0 (&tty->termios_rwsem){++++..}: [] __lock_acquire+0x1c43/0x1d30 [] lock_acquire+0x92/0x1f0 [] down_write+0x44/0x70 [] tty_do_resize+0x36/0xe0 [] vc_do_resize+0x3e1/0x4c0 [] vc_resize+0x1f/0x30 [] fbcon_init+0x385/0x5a0 [] visual_init+0xbc/0x120 [] do_bind_con_driver+0x163/0x320 [] do_take_over_console+0x61/0x70 [] do_fbcon_takeover+0x63/0xc0 [] fbcon_event_notify+0x715/0x820 [] notifier_call_chain+0x5d/0x110 [] __blocking_notifier_call_chain+0x6c/0xc0 [] blocking_notifier_call_chain+0x16/0x20 [] fb_notifier_call_chain+0x1b/0x20 [] register_framebuffer+0x1e2/0x320 [] drm_fb_helper_initial_config+0x371/0x540 [drm_kms_helper] [] nouveau_fbcon_init+0x105/0x140 [nouveau] [] nouveau_drm_load+0x43f/0x610 [nouveau] [] drm_get_pci_dev+0x17e/0x2a0 [drm] [] nouveau_drm_probe+0x25a/0x2a0 [nouveau] [] local_pci_probe+0x4b/0x80 [] pci_device_probe+0x111/0x120 [] driver_probe_device+0x8b/0x3a0 [] __driver_attach+0xab/0xb0 [] bus_for_each_dev+0x5d/0xa0 [] driver_attach+0x1e/0x20 [] bus_add_driver+0x111/0x290 [] driver_register+0x77/0x170 [] __pci_register_driver+0x64/0x70 [] drm_pci_init+0x11a/0x130 [drm] [] nouveau_drm_init+0x4d/0x1000 [nouveau] [] do_one_initcall+0xea/0x1a0 [] load_module+0x123b/0x1bf0 [] SyS_init_module+0xd7/0x120 [] system_call_fastpath+0x16/0x1b other info that might help us debug this: Chain exists of: &tty->termios_rwsem --> console_lock --> (fb_notifier_list).rwsem Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock((fb_notifier_list).rwsem); lock(console_lock); lock((fb_notifier_list).rwsem); lock(&tty->termios_rwsem); *** DEADLOCK *** 7 locks held by modprobe/277: #0: (&__lockdep_no_validate__){......}, at: [] __driver_attach+0x5b/0xb0 #1: (&__lockdep_no_validate__){......}, at: [] __driver_attach+0x69/0xb0 #2: (drm_global_mutex){+.+.+.}, at: [] drm_get_pci_dev+0xbd/0x2a0 [drm] #3: (registration_lock){+.+.+.}, at: [] register_framebuffer+0x25/0x320 #4: (&fb_info->lock){+.+.+.}, at: [] lock_fb_info+0x26/0x60 #5: (console_lock){+.+.+.}, at: [] register_framebuffer+0x1d4/0x320 #6: ((fb_notifier_list).rwsem){.+.+.+}, at: [] __blocking_notifier_call_chain+0x56/0xc0 stack backtrace: CPU: 0 PID: 277 Comm: modprobe Not tainted 3.10.0-0+tip-xeon+lockdep #0+tip Hardware name: Dell Inc. Precision WorkStation T5400 /0RW203, BIOS A11 04/30/2012 ffffffff8213e5e0 ffff8802aa2fb298 ffffffff81755f19 ffff8802aa2fb2e8 ffffffff8174f506 ffff8802aa2fa000 ffff8802aa2fb378 ffff8802aa2ea8e8 ffff8802aa2ea910 ffff8802aa2ea8e8 0000000000000006 0000000000000007 Call Trace: [] dump_stack+0x19/0x1b [] print_circular_bug+0x1fb/0x20c [] __lock_acquire+0x1c43/0x1d30 [] ? mark_held_locks+0xae/0x120 [] ? trace_hardirqs_on_caller+0x105/0x1d0 [] lock_acquire+0x92/0x1f0 [] ? tty_do_resize+0x36/0xe0 [] down_write+0x44/0x70 [] ? tty_do_resize+0x36/0xe0 [] tty_do_resize+0x36/0xe0 [] vc_do_resize+0x3e1/0x4c0 [] vc_resize+0x1f/0x30 [] fbcon_init+0x385/0x5a0 [] visual_init+0xbc/0x120 [] do_bind_con_driver+0x163/0x320 [] do_take_over_console+0x61/0x70 [] do_fbcon_takeover+0x63/0xc0 [] fbcon_event_notify+0x715/0x820 [] notifier_call_chain+0x5d/0x110 [] __blocking_notifier_call_chain+0x6c/0xc0 [] blocking_notifier_call_chain+0x16/0x20 [] fb_notifier_call_chain+0x1b/0x20 [] register_framebuffer+0x1e2/0x320 [] drm_fb_helper_initial_config+0x371/0x540 [drm_kms_helper] [] ? kmemleak_alloc+0x5b/0xc0 [] ? kmem_cache_alloc_trace+0x104/0x290 [] ? drm_fb_helper_single_add_all_connectors+0x81/0xf0 [drm_kms_helper] [] nouveau_fbcon_init+0x105/0x140 [nouveau] [] nouveau_drm_load+0x43f/0x610 [nouveau] [] drm_get_pci_dev+0x17e/0x2a0 [drm] [] nouveau_drm_probe+0x25a/0x2a0 [nouveau] [] ? _raw_spin_unlock_irqrestore+0x42/0x80 [] local_pci_probe+0x4b/0x80 [] pci_device_probe+0x111/0x120 [] driver_probe_device+0x8b/0x3a0 [] __driver_attach+0xab/0xb0 [] ? driver_probe_device+0x3a0/0x3a0 [] bus_for_each_dev+0x5d/0xa0 [] driver_attach+0x1e/0x20 [] bus_add_driver+0x111/0x290 [] ? 0xffffffffa0229fff [] driver_register+0x77/0x170 [] ? 0xffffffffa0229fff [] __pci_register_driver+0x64/0x70 [] drm_pci_init+0x11a/0x130 [drm] [] ? 0xffffffffa0229fff [] ? 0xffffffffa0229fff [] nouveau_drm_init+0x4d/0x1000 [nouveau] [] do_one_initcall+0xea/0x1a0 [] load_module+0x123b/0x1bf0 [] ? ddebug_proc_open+0xb0/0xb0 [] ? trace_hardirqs_on_thunk+0x3a/0x3f [] SyS_init_module+0xd7/0x120 [] system_call_fastpath+0x16/0x1b Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 4 ++-- drivers/tty/tty_io.c | 11 ++++++----- include/linux/tty.h | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index b940127ba1c8..25c9bc783722 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -281,7 +281,7 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws) struct tty_struct *pty = tty->link; /* For a PTY we need to lock the tty side */ - down_write(&tty->termios_rwsem); + mutex_lock(&tty->winsize_mutex); if (!memcmp(ws, &tty->winsize, sizeof(*ws))) goto done; @@ -308,7 +308,7 @@ static int pty_resize(struct tty_struct *tty, struct winsize *ws) tty->winsize = *ws; pty->winsize = *ws; /* Never used so will go away soon */ done: - up_write(&tty->termios_rwsem); + mutex_unlock(&tty->winsize_mutex); return 0; } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 2174698dd6f7..26bb78c30a00 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2229,7 +2229,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p) * * Copies the kernel idea of the window size into the user buffer. * - * Locking: tty->termios_rwsem is taken to ensure the winsize data + * Locking: tty->winsize_mutex is taken to ensure the winsize data * is consistent. */ @@ -2237,9 +2237,9 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg) { int err; - down_read(&tty->termios_rwsem); + mutex_lock(&tty->winsize_mutex); err = copy_to_user(arg, &tty->winsize, sizeof(*arg)); - up_read(&tty->termios_rwsem); + mutex_unlock(&tty->winsize_mutex); return err ? -EFAULT: 0; } @@ -2260,7 +2260,7 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws) unsigned long flags; /* Lock the tty */ - down_write(&tty->termios_rwsem); + mutex_lock(&tty->winsize_mutex); if (!memcmp(ws, &tty->winsize, sizeof(*ws))) goto done; /* Get the PID values and reference them so we can @@ -2275,7 +2275,7 @@ int tty_do_resize(struct tty_struct *tty, struct winsize *ws) tty->winsize = *ws; done: - up_write(&tty->termios_rwsem); + mutex_unlock(&tty->winsize_mutex); return 0; } EXPORT_SYMBOL(tty_do_resize); @@ -3016,6 +3016,7 @@ void initialize_tty_struct(struct tty_struct *tty, mutex_init(&tty->legacy_mutex); mutex_init(&tty->throttle_mutex); init_rwsem(&tty->termios_rwsem); + mutex_init(&tty->winsize_mutex); init_ldsem(&tty->ldisc_sem); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); diff --git a/include/linux/tty.h b/include/linux/tty.h index 554b732d8b55..64f864651d86 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -245,6 +245,7 @@ struct tty_struct { struct mutex legacy_mutex; struct mutex throttle_mutex; struct rw_semaphore termios_rwsem; + struct mutex winsize_mutex; spinlock_t ctrl_lock; /* Termios values are protected by the termios rwsem */ struct ktermios termios, termios_locked; @@ -254,7 +255,7 @@ struct tty_struct { struct pid *session; unsigned long flags; int count; - struct winsize winsize; /* termios rwsem */ + struct winsize winsize; /* winsize_mutex */ unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1; unsigned char ctrl_status; /* ctrl_lock */ unsigned int receive_room; /* Bytes free for queue */ -- cgit v1.2.3-71-gd317 From 10e232c597ac757e7f8600649f7e872e86de190f Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Fri, 28 Jun 2013 09:38:12 +0800 Subject: USB: check sg buffer size in usb_submit_urb USB spec stats that short packet can only appear at the end of transfer. Because lost of HC(EHCI/UHCI/OHCI/...) can't build a full packet from discontinuous buffers, we introduce the limit in usb_submit_urb() to avoid such kind of bad sg buffers coming from driver. The limit might be a bit strict: - platform has iommu to do sg list mapping - some host controllers may support to build full packet from discontinuous buffers. But considered that most of HCs don't support that, and driver need work well or keep consistent on different HCs and ARCHs, we have to introduce the limit. Currently, only usbtest is reported to pass such sg buffers to HC, and other users(mass storage, usbfs) don't have the problem. We don't check it on USB wireless device, because: - wireless devices can't be attached to common USB bus(EHCI/UHCI/OHCI/...) - the max packet size of endpoint may be odd, and often can't devide 4KB which is a typical usage in usb mass storage application Reported-by: Konstantin Filatov Reported-by: Denis V. Lunev Cc: Felipe Balbi Acked-by: Alan Stern Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/urb.c | 8 ++++++++ include/linux/usb.h | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 16927fa88fbd..e75115a04a2e 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -7,6 +7,7 @@ #include #include #include +#include #define to_urb(d) container_of(d, struct urb, kref) @@ -413,6 +414,13 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) urb->iso_frame_desc[n].status = -EXDEV; urb->iso_frame_desc[n].actual_length = 0; } + } else if (dev->speed != USB_SPEED_WIRELESS && urb->num_sgs) { + struct scatterlist *sg; + int i; + + for_each_sg(urb->sg, sg, urb->num_sgs - 1, i) + if (sg->length % max) + return -EINVAL; } /* the I/O buffer must be mapped/unmapped, except when length=0 */ diff --git a/include/linux/usb.h b/include/linux/usb.h index a232b7ece1f6..e99b2a114af4 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1258,7 +1258,9 @@ typedef void (*usb_complete_t)(struct urb *); * the device driver is saying that it provided this DMA address, * which the host controller driver should use in preference to the * transfer_buffer. - * @sg: scatter gather buffer list + * @sg: scatter gather buffer list, the buffer size of each element in + * the list (except the last) must be divisible by the endpoint's + * max packet size * @num_mapped_sgs: (internal) number of mapped sg entries * @num_sgs: number of entries in the sg list * @transfer_buffer_length: How big is transfer_buffer. The transfer may -- cgit v1.2.3-71-gd317 From 91705c61b52029ab5da67a15a23eef08667bf40e Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 23 Jul 2013 14:51:47 +0200 Subject: net: sctp: trivial: update mailing list address The SCTP mailing list address to send patches or questions to is linux-sctp@vger.kernel.org and not lksctp-developers@lists.sourceforge.net anymore. Therefore, update all occurences. Signed-off-by: Daniel Borkmann Acked-by: Neil Horman Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- Documentation/networking/sctp.txt | 5 +---- include/net/sctp/auth.h | 2 +- include/net/sctp/checksum.h | 2 +- include/net/sctp/constants.h | 2 +- include/net/sctp/sctp.h | 2 +- include/net/sctp/sm.h | 2 +- include/net/sctp/structs.h | 2 +- include/net/sctp/tsnmap.h | 2 +- include/net/sctp/ulpevent.h | 2 +- include/net/sctp/ulpqueue.h | 2 +- include/uapi/linux/sctp.h | 2 +- net/sctp/associola.c | 2 +- net/sctp/auth.c | 2 +- net/sctp/bind_addr.c | 2 +- net/sctp/chunk.c | 2 +- net/sctp/command.c | 2 +- net/sctp/debug.c | 2 +- net/sctp/endpointola.c | 2 +- net/sctp/input.c | 2 +- net/sctp/inqueue.c | 2 +- net/sctp/ipv6.c | 2 +- net/sctp/objcnt.c | 2 +- net/sctp/output.c | 2 +- net/sctp/outqueue.c | 2 +- net/sctp/primitive.c | 2 +- net/sctp/proc.c | 2 +- net/sctp/protocol.c | 4 ++-- net/sctp/sm_make_chunk.c | 2 +- net/sctp/sm_sideeffect.c | 2 +- net/sctp/sm_statefuns.c | 2 +- net/sctp/sm_statetable.c | 2 +- net/sctp/socket.c | 2 +- net/sctp/ssnmap.c | 2 +- net/sctp/sysctl.c | 2 +- net/sctp/transport.c | 2 +- net/sctp/tsnmap.c | 2 +- net/sctp/ulpevent.c | 2 +- net/sctp/ulpqueue.c | 2 +- 38 files changed, 39 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/Documentation/networking/sctp.txt b/Documentation/networking/sctp.txt index 0c790a76910e..97b810ca9082 100644 --- a/Documentation/networking/sctp.txt +++ b/Documentation/networking/sctp.txt @@ -19,7 +19,6 @@ of SCTP that is RFC 2960 compliant and provides an programming interface referred to as the UDP-style API of the Sockets Extensions for SCTP, as proposed in IETF Internet-Drafts. - Caveats: -lksctp can be built as statically or as a module. However, be aware that @@ -33,6 +32,4 @@ For more information, please visit the lksctp project website: http://www.sf.net/projects/lksctp Or contact the lksctp developers through the mailing list: - - - + diff --git a/include/net/sctp/auth.h b/include/net/sctp/auth.h index 49bc9577c61e..754c511f4cf9 100644 --- a/include/net/sctp/auth.h +++ b/include/net/sctp/auth.h @@ -22,7 +22,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/include/net/sctp/checksum.h b/include/net/sctp/checksum.h index 0cb08e6fb6df..78b88aa8c810 100644 --- a/include/net/sctp/checksum.h +++ b/include/net/sctp/checksum.h @@ -25,7 +25,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index ca50e0751e47..a1e7aa5c8bbf 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -25,7 +25,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index d8e37ecea691..554cf88605fc 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -27,7 +27,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 2a82d1384706..2aa66dda4a5a 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -27,7 +27,7 @@ * * Please send any bug reports or fixes you make to the * email addresses: - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index e745c92a1532..75c4c16601b6 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -25,7 +25,7 @@ * * Please send any bug reports or fixes you make to the * email addresses: - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/include/net/sctp/tsnmap.h b/include/net/sctp/tsnmap.h index 2c5d2b4d5d1e..c361d6f7e0cb 100644 --- a/include/net/sctp/tsnmap.h +++ b/include/net/sctp/tsnmap.h @@ -28,7 +28,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h index ca4693b4e09e..34f0d34d22ce 100644 --- a/include/net/sctp/ulpevent.h +++ b/include/net/sctp/ulpevent.h @@ -31,7 +31,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/include/net/sctp/ulpqueue.h b/include/net/sctp/ulpqueue.h index 00e50ba3f24b..add3237a9fdb 100644 --- a/include/net/sctp/ulpqueue.h +++ b/include/net/sctp/ulpqueue.h @@ -30,7 +30,7 @@ * * Please send any bug reports or fixes you make to the * email addresses: - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 66b466e4ca08..ca451e99b28b 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -28,7 +28,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/associola.c b/net/sctp/associola.c index bce5b79662a6..e425ba0bd79e 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -28,7 +28,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/auth.c b/net/sctp/auth.c index ba1dfc3f8def..3aab967081be 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -22,7 +22,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 64977ea0f9c5..f34ce8bc1395 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -27,7 +27,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 5780565f5b7d..b50b90c16a86 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -24,7 +24,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/command.c b/net/sctp/command.c index c0044019db9e..f4bebdadf007 100644 --- a/net/sctp/command.c +++ b/net/sctp/command.c @@ -25,7 +25,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/debug.c b/net/sctp/debug.c index f4998780d6df..44aa4e7543a9 100644 --- a/net/sctp/debug.c +++ b/net/sctp/debug.c @@ -28,7 +28,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 9e3d257de0e0..825b7543a436 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -29,7 +29,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/input.c b/net/sctp/input.c index 3fa4d858c35a..7993495a4c0f 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -29,7 +29,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c index cb25f040fed0..e70f60ae92ef 100644 --- a/net/sctp/inqueue.c +++ b/net/sctp/inqueue.c @@ -30,7 +30,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 09ffcc912d23..85d688f59e6a 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -27,7 +27,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/objcnt.c b/net/sctp/objcnt.c index fe012c44f8df..aec346cbed67 100644 --- a/net/sctp/objcnt.c +++ b/net/sctp/objcnt.c @@ -26,7 +26,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/output.c b/net/sctp/output.c index a46d1eb41762..5a55c55d71ad 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -26,7 +26,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index ef9e2bbc0f2f..51313233e635 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -28,7 +28,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/primitive.c b/net/sctp/primitive.c index 794bb14decde..31fa437da117 100644 --- a/net/sctp/primitive.c +++ b/net/sctp/primitive.c @@ -29,7 +29,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/proc.c b/net/sctp/proc.c index 62526c477050..aff0cac45b64 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -22,7 +22,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 4a17494d736c..b52ec2510101 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -29,7 +29,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp @@ -1547,7 +1547,7 @@ module_exit(sctp_exit); */ MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-132"); MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-132"); -MODULE_AUTHOR("Linux Kernel SCTP developers "); +MODULE_AUTHOR("Linux Kernel SCTP developers "); MODULE_DESCRIPTION("Support for the SCTP protocol (RFC2960)"); module_param_named(no_checksums, sctp_checksum_disable, bool, 0644); MODULE_PARM_DESC(no_checksums, "Disable checksums computing and verification"); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 362ae6e2fd93..780a2d4b98a2 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -29,7 +29,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 9da68852ee94..f1f3aac3f3bb 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -28,7 +28,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index f6b7109195a6..93271f0966c6 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -28,7 +28,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index 84d98d8a5a74..64ea5fd87eb2 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -28,7 +28,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/socket.c b/net/sctp/socket.c index c6670d2e3f8d..02457123bdee 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -34,7 +34,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/ssnmap.c b/net/sctp/ssnmap.c index da8603523808..72b5939101bb 100644 --- a/net/sctp/ssnmap.c +++ b/net/sctp/ssnmap.c @@ -24,7 +24,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 9a5c4c9eddaf..190674702b20 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -25,7 +25,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/transport.c b/net/sctp/transport.c index bdbbc3fd7c14..9602c52aa617 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -30,7 +30,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/tsnmap.c b/net/sctp/tsnmap.c index b46019568a86..0eff8667d4c7 100644 --- a/net/sctp/tsnmap.c +++ b/net/sctp/tsnmap.c @@ -27,7 +27,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 44a45dbee4df..c07624fca7e5 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -28,7 +28,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index 04e3d470f877..2cdb3010f50f 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -27,7 +27,7 @@ * * Please send any bug reports or fixes you make to the * email address(es): - * lksctp developers + * lksctp developers * * Or submit a bug report through the following website: * http://www.sf.net/projects/lksctp -- cgit v1.2.3-71-gd317 From 64dc61306ce7da370833289739e2f52dfc6b37ba Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 22 Jul 2013 20:26:31 -0700 Subject: net: add sk_stream_is_writeable() helper Several call sites use the hardcoded following condition : sk_stream_wspace(sk) >= sk_stream_min_wspace(sk) Lets use a helper because TCP_NOTSENT_LOWAT support will change this condition for TCP sockets. Signed-off-by: Eric Dumazet Cc: Neal Cardwell Cc: Yuchung Cheng Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/sock.h | 4 ++++ net/ceph/messenger.c | 2 +- net/core/stream.c | 2 +- net/dccp/proto.c | 4 ++-- net/ipv4/tcp.c | 4 ++-- net/sunrpc/svcsock.c | 2 +- net/sunrpc/xprtsock.c | 2 +- 7 files changed, 12 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index e0473f61693f..d0b5fdee50a2 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1088,6 +1088,10 @@ static inline struct cg_proto *parent_cg_proto(struct proto *proto, } #endif +static inline bool sk_stream_is_writeable(const struct sock *sk) +{ + return sk_stream_wspace(sk) >= sk_stream_min_wspace(sk); +} static inline bool sk_has_memory_pressure(const struct sock *sk) { diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index eb0a46a49bd4..3be308e14302 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -409,7 +409,7 @@ static void ceph_sock_write_space(struct sock *sk) * and net/core/stream.c:sk_stream_write_space(). */ if (con_flag_test(con, CON_FLAG_WRITE_PENDING)) { - if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) { + if (sk_stream_is_writeable(sk)) { dout("%s %p queueing write work\n", __func__, con); clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags); queue_con(con); diff --git a/net/core/stream.c b/net/core/stream.c index f5df85dcd20b..512f0a24269b 100644 --- a/net/core/stream.c +++ b/net/core/stream.c @@ -30,7 +30,7 @@ void sk_stream_write_space(struct sock *sk) struct socket *sock = sk->sk_socket; struct socket_wq *wq; - if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk) && sock) { + if (sk_stream_is_writeable(sk) && sock) { clear_bit(SOCK_NOSPACE, &sock->flags); rcu_read_lock(); diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 6c7c78b83940..ba64750f0387 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -336,7 +336,7 @@ unsigned int dccp_poll(struct file *file, struct socket *sock, mask |= POLLIN | POLLRDNORM; if (!(sk->sk_shutdown & SEND_SHUTDOWN)) { - if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) { + if (sk_stream_is_writeable(sk)) { mask |= POLLOUT | POLLWRNORM; } else { /* send SIGIO later */ set_bit(SOCK_ASYNC_NOSPACE, @@ -347,7 +347,7 @@ unsigned int dccp_poll(struct file *file, struct socket *sock, * wspace test but before the flags are set, * IO signal will be lost. */ - if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) + if (sk_stream_is_writeable(sk)) mask |= POLLOUT | POLLWRNORM; } } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 5423223e93c2..5eca9060bb8e 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -499,7 +499,7 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait) mask |= POLLIN | POLLRDNORM; if (!(sk->sk_shutdown & SEND_SHUTDOWN)) { - if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) { + if (sk_stream_is_writeable(sk)) { mask |= POLLOUT | POLLWRNORM; } else { /* send SIGIO later */ set_bit(SOCK_ASYNC_NOSPACE, @@ -510,7 +510,7 @@ unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait) * wspace test but before the flags are set, * IO signal will be lost. */ - if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) + if (sk_stream_is_writeable(sk)) mask |= POLLOUT | POLLWRNORM; } } else diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 305374d4fb98..0da6785ec15a 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -442,7 +442,7 @@ static void svc_tcp_write_space(struct sock *sk) { struct socket *sock = sk->sk_socket; - if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk) && sock) + if (sk_stream_is_writeable(sk) && sock) clear_bit(SOCK_NOSPACE, &sock->flags); svc_write_space(sk); } diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index ddf0602603bd..d6656d7768f4 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1602,7 +1602,7 @@ static void xs_tcp_write_space(struct sock *sk) read_lock_bh(&sk->sk_callback_lock); /* from net/core/stream.c:sk_stream_write_space */ - if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) + if (sk_stream_is_writeable(sk)) xs_write_space(sk); read_unlock_bh(&sk->sk_callback_lock); -- cgit v1.2.3-71-gd317 From c9bee3b7fdecb0c1d070c7b54113b3bdfb9a3d36 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 22 Jul 2013 20:27:07 -0700 Subject: tcp: TCP_NOTSENT_LOWAT socket option Idea of this patch is to add optional limitation of number of unsent bytes in TCP sockets, to reduce usage of kernel memory. TCP receiver might announce a big window, and TCP sender autotuning might allow a large amount of bytes in write queue, but this has little performance impact if a large part of this buffering is wasted : Write queue needs to be large only to deal with large BDP, not necessarily to cope with scheduling delays (incoming ACKS make room for the application to queue more bytes) For most workloads, using a value of 128 KB or less is OK to give applications enough time to react to POLLOUT events in time (or being awaken in a blocking sendmsg()) This patch adds two ways to set the limit : 1) Per socket option TCP_NOTSENT_LOWAT 2) A sysctl (/proc/sys/net/ipv4/tcp_notsent_lowat) for sockets not using TCP_NOTSENT_LOWAT socket option (or setting a zero value) Default value being UINT_MAX (0xFFFFFFFF), meaning this has no effect. This changes poll()/select()/epoll() to report POLLOUT only if number of unsent bytes is below tp->nosent_lowat Note this might increase number of sendmsg()/sendfile() calls when using non blocking sockets, and increase number of context switches for blocking sockets. Note this is not related to SO_SNDLOWAT (as SO_SNDLOWAT is defined as : Specify the minimum number of bytes in the buffer until the socket layer will pass the data to the protocol) Tested: netperf sessions, and watching /proc/net/protocols "memory" column for TCP With 200 concurrent netperf -t TCP_STREAM sessions, amount of kernel memory used by TCP buffers shrinks by ~55 % (20567 pages instead of 45458) lpq83:~# echo -1 >/proc/sys/net/ipv4/tcp_notsent_lowat lpq83:~# (super_netperf 200 -t TCP_STREAM -H remote -l 90 &); sleep 60 ; grep TCP /proc/net/protocols TCPv6 1880 2 45458 no 208 yes ipv6 y y y y y y y y y y y y y n y y y y y TCP 1696 508 45458 no 208 yes kernel y y y y y y y y y y y y y n y y y y y lpq83:~# echo 131072 >/proc/sys/net/ipv4/tcp_notsent_lowat lpq83:~# (super_netperf 200 -t TCP_STREAM -H remote -l 90 &); sleep 60 ; grep TCP /proc/net/protocols TCPv6 1880 2 20567 no 208 yes ipv6 y y y y y y y y y y y y y n y y y y y TCP 1696 508 20567 no 208 yes kernel y y y y y y y y y y y y y n y y y y y Using 128KB has no bad effect on the throughput or cpu usage of a single flow, although there is an increase of context switches. A bonus is that we hold socket lock for a shorter amount of time and should improve latencies of ACK processing. lpq83:~# echo -1 >/proc/sys/net/ipv4/tcp_notsent_lowat lpq83:~# perf stat -e context-switches ./netperf -H 7.7.7.84 -t omni -l 20 -c -i10,3 OMNI Send TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.7.84 () port 0 AF_INET : +/-2.500% @ 99% conf. Local Remote Local Elapsed Throughput Throughput Local Local Remote Remote Local Remote Service Send Socket Recv Socket Send Time Units CPU CPU CPU CPU Service Service Demand Size Size Size (sec) Util Util Util Util Demand Demand Units Final Final % Method % Method 1651584 6291456 16384 20.00 17447.90 10^6bits/s 3.13 S -1.00 U 0.353 -1.000 usec/KB Performance counter stats for './netperf -H 7.7.7.84 -t omni -l 20 -c -i10,3': 412,514 context-switches 200.034645535 seconds time elapsed lpq83:~# echo 131072 >/proc/sys/net/ipv4/tcp_notsent_lowat lpq83:~# perf stat -e context-switches ./netperf -H 7.7.7.84 -t omni -l 20 -c -i10,3 OMNI Send TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.7.84 () port 0 AF_INET : +/-2.500% @ 99% conf. Local Remote Local Elapsed Throughput Throughput Local Local Remote Remote Local Remote Service Send Socket Recv Socket Send Time Units CPU CPU CPU CPU Service Service Demand Size Size Size (sec) Util Util Util Util Demand Demand Units Final Final % Method % Method 1593240 6291456 16384 20.00 17321.16 10^6bits/s 3.35 S -1.00 U 0.381 -1.000 usec/KB Performance counter stats for './netperf -H 7.7.7.84 -t omni -l 20 -c -i10,3': 2,675,818 context-switches 200.029651391 seconds time elapsed Signed-off-by: Eric Dumazet Cc: Neal Cardwell Cc: Yuchung Cheng Acked-By: Yuchung Cheng Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 13 +++++++++++++ include/linux/tcp.h | 1 + include/net/sock.h | 19 +++++++++++++------ include/net/tcp.h | 14 ++++++++++++++ include/uapi/linux/tcp.h | 1 + net/ipv4/sysctl_net_ipv4.c | 7 +++++++ net/ipv4/tcp.c | 7 +++++++ net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_output.c | 3 +++ net/ipv6/tcp_ipv6.c | 1 + 10 files changed, 61 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 10742902146f..53cea9bcb14c 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -516,6 +516,19 @@ tcp_wmem - vector of 3 INTEGERs: min, default, max this value is ignored. Default: between 64K and 4MB, depending on RAM size. +tcp_notsent_lowat - UNSIGNED INTEGER + A TCP socket can control the amount of unsent bytes in its write queue, + thanks to TCP_NOTSENT_LOWAT socket option. poll()/select()/epoll() + reports POLLOUT events if the amount of unsent bytes is below a per + socket value, and if the write queue is not full. sendmsg() will + also not add new buffers if the limit is hit. + + This global variable controls the amount of unsent data for + sockets not using TCP_NOTSENT_LOWAT. For these sockets, a change + to the global variable has immediate effect. + + Default: UINT_MAX (0xFFFFFFFF) + tcp_workaround_signed_windows - BOOLEAN If set, assume no receipt of a window scaling option means the remote TCP is broken and treats the window as a signed quantity. diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 472120b4fac5..9640803a17a7 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -238,6 +238,7 @@ struct tcp_sock { u32 rcv_wnd; /* Current receiver window */ u32 write_seq; /* Tail(+1) of data held in tcp send buffer */ + u32 notsent_lowat; /* TCP_NOTSENT_LOWAT */ u32 pushed_seq; /* Last pushed seq, required to talk to windows */ u32 lost_out; /* Lost packets */ u32 sacked_out; /* SACK'd packets */ diff --git a/include/net/sock.h b/include/net/sock.h index d0b5fdee50a2..b9f2b095b1ab 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -746,11 +746,6 @@ static inline int sk_stream_wspace(const struct sock *sk) extern void sk_stream_write_space(struct sock *sk); -static inline bool sk_stream_memory_free(const struct sock *sk) -{ - return sk->sk_wmem_queued < sk->sk_sndbuf; -} - /* OOB backlog add */ static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb) { @@ -950,6 +945,7 @@ struct proto { unsigned int inuse_idx; #endif + bool (*stream_memory_free)(const struct sock *sk); /* Memory pressure */ void (*enter_memory_pressure)(struct sock *sk); atomic_long_t *memory_allocated; /* Current allocated memory. */ @@ -1088,11 +1084,22 @@ static inline struct cg_proto *parent_cg_proto(struct proto *proto, } #endif +static inline bool sk_stream_memory_free(const struct sock *sk) +{ + if (sk->sk_wmem_queued >= sk->sk_sndbuf) + return false; + + return sk->sk_prot->stream_memory_free ? + sk->sk_prot->stream_memory_free(sk) : true; +} + static inline bool sk_stream_is_writeable(const struct sock *sk) { - return sk_stream_wspace(sk) >= sk_stream_min_wspace(sk); + return sk_stream_wspace(sk) >= sk_stream_min_wspace(sk) && + sk_stream_memory_free(sk); } + static inline bool sk_has_memory_pressure(const struct sock *sk) { return sk->sk_prot->memory_pressure != NULL; diff --git a/include/net/tcp.h b/include/net/tcp.h index c5868471abae..18fc999dae3c 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -284,6 +284,7 @@ extern int sysctl_tcp_thin_dupack; extern int sysctl_tcp_early_retrans; extern int sysctl_tcp_limit_output_bytes; extern int sysctl_tcp_challenge_ack_limit; +extern unsigned int sysctl_tcp_notsent_lowat; extern atomic_long_t tcp_memory_allocated; extern struct percpu_counter tcp_sockets_allocated; @@ -1539,6 +1540,19 @@ extern int tcp_gro_complete(struct sk_buff *skb); extern void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr); +static inline u32 tcp_notsent_lowat(const struct tcp_sock *tp) +{ + return tp->notsent_lowat ?: sysctl_tcp_notsent_lowat; +} + +static inline bool tcp_stream_memory_free(const struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + u32 notsent_bytes = tp->write_seq - tp->snd_nxt; + + return notsent_bytes < tcp_notsent_lowat(tp); +} + #ifdef CONFIG_PROC_FS extern int tcp4_proc_init(void); extern void tcp4_proc_exit(void); diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 8d776ebc4829..377f1e59411d 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -111,6 +111,7 @@ enum { #define TCP_REPAIR_OPTIONS 22 #define TCP_FASTOPEN 23 /* Enable FastOpen on listeners */ #define TCP_TIMESTAMP 24 +#define TCP_NOTSENT_LOWAT 25 /* limit number of unsent bytes in write queue */ struct tcp_repair_opt { __u32 opt_code; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index b2c123c44d69..69ed203802da 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -554,6 +554,13 @@ static struct ctl_table ipv4_table[] = { .proc_handler = proc_dointvec_minmax, .extra1 = &one, }, + { + .procname = "tcp_notsent_lowat", + .data = &sysctl_tcp_notsent_lowat, + .maxlen = sizeof(sysctl_tcp_notsent_lowat), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { .procname = "tcp_rmem", .data = &sysctl_tcp_rmem, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 5eca9060bb8e..c27e81392398 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2631,6 +2631,10 @@ static int do_tcp_setsockopt(struct sock *sk, int level, else tp->tsoffset = val - tcp_time_stamp; break; + case TCP_NOTSENT_LOWAT: + tp->notsent_lowat = val; + sk->sk_write_space(sk); + break; default: err = -ENOPROTOOPT; break; @@ -2847,6 +2851,9 @@ static int do_tcp_getsockopt(struct sock *sk, int level, case TCP_TIMESTAMP: val = tcp_time_stamp + tp->tsoffset; break; + case TCP_NOTSENT_LOWAT: + val = tp->notsent_lowat; + break; default: return -ENOPROTOOPT; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 2e3f129df0eb..2a5d5c469d17 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2800,6 +2800,7 @@ struct proto tcp_prot = { .unhash = inet_unhash, .get_port = inet_csk_get_port, .enter_memory_pressure = tcp_enter_memory_pressure, + .stream_memory_free = tcp_stream_memory_free, .sockets_allocated = &tcp_sockets_allocated, .orphan_count = &tcp_orphan_count, .memory_allocated = &tcp_memory_allocated, diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 92fde8d1aa82..884efff5b531 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -65,6 +65,9 @@ int sysctl_tcp_base_mss __read_mostly = TCP_BASE_MSS; /* By default, RFC2861 behavior. */ int sysctl_tcp_slow_start_after_idle __read_mostly = 1; +unsigned int sysctl_tcp_notsent_lowat __read_mostly = UINT_MAX; +EXPORT_SYMBOL(sysctl_tcp_notsent_lowat); + static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, int push_one, gfp_t gfp); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 80fe69ef2188..b792e870686b 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1924,6 +1924,7 @@ struct proto tcpv6_prot = { .unhash = inet_unhash, .get_port = inet_csk_get_port, .enter_memory_pressure = tcp_enter_memory_pressure, + .stream_memory_free = tcp_stream_memory_free, .sockets_allocated = &tcp_sockets_allocated, .memory_allocated = &tcp_memory_allocated, .memory_pressure = &tcp_memory_pressure, -- cgit v1.2.3-71-gd317 From 18afa4b028b46f8b45ca64f94aefe717c297b07d Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 23 Jul 2013 16:13:17 +0200 Subject: net: Make devnet_rename_seq static No users outside net/core/dev.c. Signed-off-by: Thomas Gleixner Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 --- net/core/dev.c | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 2bb2357d83bb..3ca60b070ef0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1666,9 +1666,6 @@ extern int call_netdevice_notifiers(unsigned long val, struct net_device *dev); extern rwlock_t dev_base_lock; /* Device list lock */ -extern seqcount_t devnet_rename_seq; /* Device rename seq */ - - #define for_each_netdev(net, d) \ list_for_each_entry(d, &(net)->dev_base_head, dev_list) #define for_each_netdev_reverse(net, d) \ diff --git a/net/core/dev.c b/net/core/dev.c index 26755dd40daa..dfd9f5d56ae0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -174,7 +174,7 @@ static DEFINE_SPINLOCK(napi_hash_lock); static unsigned int napi_gen_id; static DEFINE_HASHTABLE(napi_hash, 8); -seqcount_t devnet_rename_seq; +static seqcount_t devnet_rename_seq; static inline void dev_base_seq_inc(struct net *net) { -- cgit v1.2.3-71-gd317 From 9548906b2bb7ff09e12c013a55d669bef2c8e121 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 25 Jul 2013 05:44:02 +0900 Subject: xattr: Constify ->name member of "struct xattr". Since everybody sets kstrdup()ed constant string to "struct xattr"->name but nobody modifies "struct xattr"->name , we can omit kstrdup() and its failure checking by constifying ->name member of "struct xattr". Signed-off-by: Tetsuo Handa Reviewed-by: Joel Becker [ocfs2] Acked-by: Serge E. Hallyn Acked-by: Casey Schaufler Acked-by: Mimi Zohar Reviewed-by: Paul Moore Tested-by: Paul Moore Acked-by: Eric Paris Signed-off-by: James Morris --- fs/ocfs2/xattr.h | 2 +- include/linux/security.h | 8 ++++---- include/linux/xattr.h | 2 +- include/uapi/linux/reiserfs_xattr.h | 2 +- security/capability.c | 2 +- security/integrity/evm/evm_main.c | 2 +- security/security.c | 8 +++----- security/selinux/hooks.c | 17 ++++++----------- security/smack/smack_lsm.c | 9 +++------ 9 files changed, 21 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/fs/ocfs2/xattr.h b/fs/ocfs2/xattr.h index e5c7f15465b4..19f134e896a9 100644 --- a/fs/ocfs2/xattr.h +++ b/fs/ocfs2/xattr.h @@ -32,7 +32,7 @@ enum ocfs2_xattr_type { struct ocfs2_security_xattr_info { int enable; - char *name; + const char *name; void *value; size_t value_len; }; diff --git a/include/linux/security.h b/include/linux/security.h index 7ce53ae1266b..9d37e2b9d3ec 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1492,7 +1492,7 @@ struct security_operations { int (*inode_alloc_security) (struct inode *inode); void (*inode_free_security) (struct inode *inode); int (*inode_init_security) (struct inode *inode, struct inode *dir, - const struct qstr *qstr, char **name, + const struct qstr *qstr, const char **name, void **value, size_t *len); int (*inode_create) (struct inode *dir, struct dentry *dentry, umode_t mode); @@ -1770,7 +1770,7 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, initxattrs initxattrs, void *fs_data); int security_old_inode_init_security(struct inode *inode, struct inode *dir, - const struct qstr *qstr, char **name, + const struct qstr *qstr, const char **name, void **value, size_t *len); int security_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode); int security_inode_link(struct dentry *old_dentry, struct inode *dir, @@ -2094,8 +2094,8 @@ static inline int security_inode_init_security(struct inode *inode, static inline int security_old_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, - char **name, void **value, - size_t *len) + const char **name, + void **value, size_t *len) { return -EOPNOTSUPP; } diff --git a/include/linux/xattr.h b/include/linux/xattr.h index fdbafc6841cf..91b0a68d38dc 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -31,7 +31,7 @@ struct xattr_handler { }; struct xattr { - char *name; + const char *name; void *value; size_t value_len; }; diff --git a/include/uapi/linux/reiserfs_xattr.h b/include/uapi/linux/reiserfs_xattr.h index d8ce17c2459a..38fdd648be21 100644 --- a/include/uapi/linux/reiserfs_xattr.h +++ b/include/uapi/linux/reiserfs_xattr.h @@ -16,7 +16,7 @@ struct reiserfs_xattr_header { }; struct reiserfs_security_handle { - char *name; + const char *name; void *value; size_t length; }; diff --git a/security/capability.c b/security/capability.c index 32b515766df1..dbeb9bc27b24 100644 --- a/security/capability.c +++ b/security/capability.c @@ -129,7 +129,7 @@ static void cap_inode_free_security(struct inode *inode) } static int cap_inode_init_security(struct inode *inode, struct inode *dir, - const struct qstr *qstr, char **name, + const struct qstr *qstr, const char **name, void **value, size_t *len) { return -EOPNOTSUPP; diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index df0fa451a871..af9b6852f4e1 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -418,7 +418,7 @@ int evm_inode_init_security(struct inode *inode, evm_xattr->value = xattr_data; evm_xattr->value_len = sizeof(*xattr_data); - evm_xattr->name = kstrdup(XATTR_EVM_SUFFIX, GFP_NOFS); + evm_xattr->name = XATTR_EVM_SUFFIX; return 0; out: kfree(xattr_data); diff --git a/security/security.c b/security/security.c index 94b35aef6871..4dc31f4f2700 100644 --- a/security/security.c +++ b/security/security.c @@ -348,10 +348,10 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, if (unlikely(IS_PRIVATE(inode))) return 0; - memset(new_xattrs, 0, sizeof new_xattrs); if (!initxattrs) return security_ops->inode_init_security(inode, dir, qstr, NULL, NULL, NULL); + memset(new_xattrs, 0, sizeof(new_xattrs)); lsm_xattr = new_xattrs; ret = security_ops->inode_init_security(inode, dir, qstr, &lsm_xattr->name, @@ -366,16 +366,14 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, goto out; ret = initxattrs(inode, new_xattrs, fs_data); out: - for (xattr = new_xattrs; xattr->name != NULL; xattr++) { - kfree(xattr->name); + for (xattr = new_xattrs; xattr->value != NULL; xattr++) kfree(xattr->value); - } return (ret == -EOPNOTSUPP) ? 0 : ret; } EXPORT_SYMBOL(security_inode_init_security); int security_old_inode_init_security(struct inode *inode, struct inode *dir, - const struct qstr *qstr, char **name, + const struct qstr *qstr, const char **name, void **value, size_t *len) { if (unlikely(IS_PRIVATE(inode))) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c956390a9136..a5091ec06aa6 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2587,7 +2587,8 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode, } static int selinux_inode_init_security(struct inode *inode, struct inode *dir, - const struct qstr *qstr, char **name, + const struct qstr *qstr, + const char **name, void **value, size_t *len) { const struct task_security_struct *tsec = current_security(); @@ -2595,7 +2596,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, struct superblock_security_struct *sbsec; u32 sid, newsid, clen; int rc; - char *namep = NULL, *context; + char *context; dsec = dir->i_security; sbsec = dir->i_sb->s_security; @@ -2631,19 +2632,13 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP)) return -EOPNOTSUPP; - if (name) { - namep = kstrdup(XATTR_SELINUX_SUFFIX, GFP_NOFS); - if (!namep) - return -ENOMEM; - *name = namep; - } + if (name) + *name = XATTR_SELINUX_SUFFIX; if (value && len) { rc = security_sid_to_context_force(newsid, &context, &clen); - if (rc) { - kfree(namep); + if (rc) return rc; - } *value = context; *len = clen; } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 3f7682a387b7..a113a779f00c 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -582,7 +582,7 @@ static void smack_inode_free_security(struct inode *inode) * Returns 0 if it all works out, -ENOMEM if there's no memory */ static int smack_inode_init_security(struct inode *inode, struct inode *dir, - const struct qstr *qstr, char **name, + const struct qstr *qstr, const char **name, void **value, size_t *len) { struct inode_smack *issp = inode->i_security; @@ -591,11 +591,8 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, char *dsp = smk_of_inode(dir); int may; - if (name) { - *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_NOFS); - if (*name == NULL) - return -ENOMEM; - } + if (name) + *name = XATTR_SMACK_SUFFIX; if (value) { rcu_read_lock(); -- cgit v1.2.3-71-gd317 From 3784b6d64dc52ed3fbebad61a85ab9b7a687a167 Mon Sep 17 00:00:00 2001 From: Robin Gong Date: Thu, 25 Jul 2013 11:33:18 +0800 Subject: regulator: pfuze100: add pfuze100 regulator driver Add pfuze100 regulator driver. Signed-off-by: Robin Gong Tested-by: Steffen Trumtrar Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/pfuze100.txt | 113 +++++ drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/pfuze100-regulator.c | 454 +++++++++++++++++++++ include/linux/regulator/pfuze100.h | 44 ++ 5 files changed, 619 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/pfuze100.txt create mode 100644 drivers/regulator/pfuze100-regulator.c create mode 100644 include/linux/regulator/pfuze100.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/regulator/pfuze100.txt b/Documentation/devicetree/bindings/regulator/pfuze100.txt new file mode 100644 index 000000000000..22e1a48f7e83 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/pfuze100.txt @@ -0,0 +1,113 @@ +PFUZE100 family of regulators + +Required properties: +- compatible: "fsl,pfuze100" +- reg: I2C slave address +- regulators: This is the list of child nodes that specify the regulator + initialization data for defined regulators. Please refer to below doc + Documentation/devicetree/bindings/regulator/regulator.txt. + + The valid names for regulators are: + sw1ab,sw1c,sw2,sw3a,sw3b,sw4,swbst,vsnvs,vrefddr,vgen1~vgen6 + +Each regulator is defined using the standard binding for regulators. + +Example: + + pmic: pfuze100@08 { + compatible = "fsl,pfuze100"; + reg = <0x08>; + + regulators { + sw1a_reg: sw1ab { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + regulator-ramp-delay = <6250>; + }; + + sw1c_reg: sw1c { + regulator-min-microvolt = <300000>; + regulator-max-microvolt = <1875000>; + regulator-boot-on; + regulator-always-on; + }; + + sw2_reg: sw2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3a_reg: sw3a { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw3b_reg: sw3b { + regulator-min-microvolt = <400000>; + regulator-max-microvolt = <1975000>; + regulator-boot-on; + regulator-always-on; + }; + + sw4_reg: sw4 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <3300000>; + }; + + swbst_reg: swbst { + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5150000>; + }; + + snvs_reg: vsnvs { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + regulator-boot-on; + regulator-always-on; + }; + + vref_reg: vrefddr { + regulator-boot-on; + regulator-always-on; + }; + + vgen1_reg: vgen1 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen2_reg: vgen2 { + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1550000>; + }; + + vgen3_reg: vgen3 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + vgen4_reg: vgen4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen5_reg: vgen5 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vgen6_reg: vgen6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; + }; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 759b601b54ef..f9d02aabe306 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -355,6 +355,13 @@ config REGULATOR_PCF50633 Say Y here to support the voltage regulators and convertors on PCF50633 +config REGULATOR_PFUZE100 + tristate "Support regulators on Freescale PFUZE100 PMIC" + depends on I2C + help + Say y here to support the regulators found on the Freescale PFUZE100 + PMIC. + config REGULATOR_RC5T583 tristate "RICOH RC5T583 Power regulators" depends on MFD_RC5T583 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index d240772d90ee..57838e2b0b8a 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o +obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c new file mode 100644 index 000000000000..fcc7cd0d25cc --- /dev/null +++ b/drivers/regulator/pfuze100-regulator.c @@ -0,0 +1,454 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PFUZE_NUMREGS 128 +#define PFUZE100_VOL_OFFSET 0 +#define PFUZE100_STANDBY_OFFSET 1 +#define PFUZE100_MODE_OFFSET 3 +#define PFUZE100_CONF_OFFSET 4 + +#define PFUZE100_DEVICEID 0x0 +#define PFUZE100_REVID 0x3 +#define PFUZE100_FABID 0x3 + +#define PFUZE100_SW1ABVOL 0x20 +#define PFUZE100_SW1CVOL 0x2e +#define PFUZE100_SW2VOL 0x35 +#define PFUZE100_SW3AVOL 0x3c +#define PFUZE100_SW3BVOL 0x43 +#define PFUZE100_SW4VOL 0x4a +#define PFUZE100_SWBSTCON1 0x66 +#define PFUZE100_VREFDDRCON 0x6a +#define PFUZE100_VSNVSVOL 0x6b +#define PFUZE100_VGEN1VOL 0x6c +#define PFUZE100_VGEN2VOL 0x6d +#define PFUZE100_VGEN3VOL 0x6e +#define PFUZE100_VGEN4VOL 0x6f +#define PFUZE100_VGEN5VOL 0x70 +#define PFUZE100_VGEN6VOL 0x71 + +struct pfuze_regulator { + struct regulator_desc desc; + unsigned char stby_reg; + unsigned char stby_mask; +}; + +struct pfuze_chip { + struct regmap *regmap; + struct device *dev; + struct pfuze_regulator regulator_descs[PFUZE100_MAX_REGULATOR]; + struct regulator_dev *regulators[PFUZE100_MAX_REGULATOR]; +}; + +static const int pfuze100_swbst[] = { + 5000000, 5050000, 5100000, 5150000, +}; + +static const int pfuze100_vsnvs[] = { + 1000000, 1100000, 1200000, 1300000, 1500000, 1800000, 3000000, +}; + +static const struct i2c_device_id pfuze_device_id[] = { + {.name = "pfuze100"}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, pfuze_device_id); + +static const struct of_device_id pfuze_dt_ids[] = { + { .compatible = "fsl,pfuze100" }, + {}, +}; +MODULE_DEVICE_TABLE(of, pfuze_dt_ids); + +static int pfuze100_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + struct pfuze_chip *pfuze100 = rdev_get_drvdata(rdev); + int id = rdev->desc->id; + unsigned int val, ramp_bits, reg; + int ret; + + if (id < PFUZE100_SWBST) { + if (id == PFUZE100_SW1AB) + reg = PFUZE100_SW1ABVOL; + else + reg = PFUZE100_SW1CVOL + (id - PFUZE100_SW1C) * 7; + regmap_read(pfuze100->regmap, reg, &val); + + if (id <= PFUZE100_SW1C) + ramp_delay = 25000 / (2 * ramp_delay); + else if (val & 0x40) + ramp_delay = 50000 / (4 * ramp_delay); + else + ramp_delay = 25000 / (2 * ramp_delay); + + ramp_bits = (ramp_delay >> 1) - (ramp_delay >> 3); + ret = regmap_update_bits(pfuze100->regmap, reg + 4 , 0xc0, + ramp_bits << 6); + if (ret < 0) + dev_err(pfuze100->dev, "ramp failed, err %d\n", ret); + } else + ret = -EACCES; + + return ret; +} + +static struct regulator_ops pfuze100_ldo_regulator_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, +}; + +static struct regulator_ops pfuze100_fixed_regulator_ops = { + .list_voltage = regulator_list_voltage_linear, +}; + +static struct regulator_ops pfuze100_sw_regulator_ops = { + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = pfuze100_set_ramp_delay, +}; + +static struct regulator_ops pfuze100_swb_regulator_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + +}; + +#define PFUZE100_FIXED_REG(_name, base, voltage) \ + [PFUZE100_ ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = 1, \ + .ops = &pfuze100_fixed_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PFUZE100_ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (voltage), \ + .enable_reg = (base), \ + .enable_mask = 0x10, \ + }, \ + } + +#define PFUZE100_SW_REG(_name, base, min, max, step) \ + [PFUZE100_ ## _name] = { \ + .desc = { \ + .name = #_name,\ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &pfuze100_sw_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PFUZE100_ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \ + .vsel_mask = 0x3f, \ + }, \ + .stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \ + .stby_mask = 0x3f, \ + } + +#define PFUZE100_SWB_REG(_name, base, mask, voltages) \ + [PFUZE100_ ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pfuze100_swb_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PFUZE100_ ## _name, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base), \ + .vsel_mask = (mask), \ + }, \ + } + +#define PFUZE100_VGEN_REG(_name, base, min, max, step) \ + [PFUZE100_ ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &pfuze100_ldo_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = PFUZE100_ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base), \ + .vsel_mask = 0xf, \ + .enable_reg = (base), \ + .enable_mask = 0x10, \ + }, \ + .stby_reg = (base), \ + .stby_mask = 0x20, \ + } + +static struct pfuze_regulator pfuze100_regulators[] = { + PFUZE100_SW_REG(SW1AB, PFUZE100_SW1ABVOL, 300000, 1875000, 25000), + PFUZE100_SW_REG(SW1C, PFUZE100_SW1CVOL, 300000, 1875000, 25000), + PFUZE100_SW_REG(SW2, PFUZE100_SW2VOL, 400000, 1975000, 25000), + PFUZE100_SW_REG(SW3A, PFUZE100_SW3AVOL, 400000, 1975000, 25000), + PFUZE100_SW_REG(SW3B, PFUZE100_SW3BVOL, 400000, 1975000, 25000), + PFUZE100_SW_REG(SW4, PFUZE100_SW4VOL, 400000, 1975000, 25000), + PFUZE100_SWB_REG(SWBST, PFUZE100_SWBSTCON1, 0x3 , pfuze100_swbst), + PFUZE100_SWB_REG(VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs), + PFUZE100_FIXED_REG(VREFDDR, PFUZE100_VREFDDRCON, 750000), + PFUZE100_VGEN_REG(VGEN1, PFUZE100_VGEN1VOL, 800000, 1550000, 50000), + PFUZE100_VGEN_REG(VGEN2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000), + PFUZE100_VGEN_REG(VGEN3, PFUZE100_VGEN3VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), +}; + +#ifdef CONFIG_OF +static struct of_regulator_match pfuze100_matches[] = { + { .name = "sw1ab", }, + { .name = "sw1c", }, + { .name = "sw2", }, + { .name = "sw3a", }, + { .name = "sw3b", }, + { .name = "sw4", }, + { .name = "swbst", }, + { .name = "vsnvs", }, + { .name = "vrefddr", }, + { .name = "vgen1", }, + { .name = "vgen2", }, + { .name = "vgen3", }, + { .name = "vgen4", }, + { .name = "vgen5", }, + { .name = "vgen6", }, +}; + +static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) +{ + struct device *dev = chip->dev; + struct device_node *np, *parent; + int ret; + + np = of_node_get(dev->parent->of_node); + if (!np) + return 0; + + parent = of_find_node_by_name(np, "regulators"); + if (!parent) { + dev_err(dev, "regulators node not found\n"); + return -EINVAL; + } + + ret = of_regulator_match(dev, parent, pfuze100_matches, + ARRAY_SIZE(pfuze100_matches)); + + of_node_put(parent); + if (ret < 0) { + dev_err(dev, "Error parsing regulator init data: %d\n", + ret); + return ret; + } + + return 0; +} + +static inline struct regulator_init_data *match_init_data(int index) +{ + return pfuze100_matches[index].init_data; +} + +static inline struct device_node *match_of_node(int index) +{ + return pfuze100_matches[index].of_node; +} +#else +static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) +{ + return NULL; +} + +static inline struct regulator_init_data *match_init_data(int index) +{ + return NULL; +} + +static inline struct device_node *match_of_node(int index) +{ + return NULL; +} +#endif + +static int pfuze_identify(struct pfuze_chip *pfuze_chip) +{ + unsigned int value; + int ret; + + ret = regmap_read(pfuze_chip->regmap, PFUZE100_DEVICEID, &value); + if (ret) + return ret; + + if (value & 0x0f) { + dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value); + return -ENODEV; + } + + ret = regmap_read(pfuze_chip->regmap, PFUZE100_REVID, &value); + if (ret) + return ret; + dev_info(pfuze_chip->dev, + "Full lay: %x, Metal lay: %x\n", + (value & 0xf0) >> 4, value & 0x0f); + + ret = regmap_read(pfuze_chip->regmap, PFUZE100_FABID, &value); + if (ret) + return ret; + dev_info(pfuze_chip->dev, "FAB: %x, FIN: %x\n", + (value & 0xc) >> 2, value & 0x3); + + return 0; +} + +static const struct regmap_config pfuze_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = PFUZE_NUMREGS, + .cache_type = REGCACHE_RBTREE, +}; + +static int pfuze100_regulator_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pfuze_chip *pfuze_chip; + struct pfuze_regulator_platform_data *pdata = + dev_get_platdata(&client->dev); + struct regulator_config config = { }; + int i, ret; + + pfuze_chip = devm_kzalloc(&client->dev, sizeof(*pfuze_chip), + GFP_KERNEL); + if (!pfuze_chip) + return -ENOMEM; + + dev_set_drvdata(&client->dev, pfuze_chip); + + memcpy(pfuze_chip->regulator_descs, pfuze100_regulators, + sizeof(pfuze_chip->regulator_descs)); + + pfuze_chip->dev = &client->dev; + + pfuze_chip->regmap = devm_regmap_init_i2c(client, &pfuze_regmap_config); + if (IS_ERR(pfuze_chip->regmap)) { + ret = PTR_ERR(pfuze_chip->regmap); + dev_err(&client->dev, + "regmap allocation failed with err %d\n", ret); + return ret; + } + + ret = pfuze_identify(pfuze_chip); + if (ret) { + dev_err(&client->dev, "unrecognized pfuze chip ID!\n"); + return ret; + } + + ret = pfuze_parse_regulators_dt(pfuze_chip); + if (ret) + return ret; + + for (i = 0; i < PFUZE100_MAX_REGULATOR; i++) { + struct regulator_init_data *init_data; + int val; + + if (pdata) + init_data = pdata->init_data[i]; + else + init_data = match_init_data(i); + + /* SW2~SW4 high bit check and modify the voltage value table */ + if (i > PFUZE100_SW1C && i < PFUZE100_SWBST) { + regmap_read(pfuze_chip->regmap, PFUZE100_SW2VOL + + (i - PFUZE100_SW2) * 7, &val); + if (val & 0x40) { + pfuze_chip->regulator_descs[i].desc.min_uV + = 800000; + pfuze_chip->regulator_descs[i].desc.uV_step + = 50000; + } + } + + config.dev = &client->dev; + config.init_data = init_data; + config.driver_data = pfuze_chip; + config.of_node = match_of_node(i); + + pfuze_chip->regulators[i] = regulator_register(&pfuze_chip + ->regulator_descs[i].desc, &config); + if (IS_ERR(pfuze_chip->regulators[i])) { + dev_err(&client->dev, "register regulator%s failed\n", + pfuze100_regulators[i].desc.name); + ret = PTR_ERR(pfuze_chip->regulators[i]); + while (--i >= 0) + regulator_unregister(pfuze_chip->regulators[i]); + return ret; + } + } + + return 0; +} + +static int pfuze100_regulator_remove(struct i2c_client *client) +{ + int i; + struct pfuze_chip *pfuze_chip = dev_get_drvdata(&client->dev); + + for (i = 0; i < PFUZE100_MAX_REGULATOR; i++) + regulator_unregister(pfuze_chip->regulators[i]); + + return 0; +} + +static struct i2c_driver pfuze_driver = { + .id_table = pfuze_device_id, + .driver = { + .name = "pfuze100-regulator", + .owner = THIS_MODULE, + .of_match_table = pfuze_dt_ids, + }, + .probe = pfuze100_regulator_probe, + .remove = pfuze100_regulator_remove, +}; +module_i2c_driver(pfuze_driver); + +MODULE_AUTHOR("Robin Gong "); +MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100 PMIC"); +MODULE_ALIAS("pfuze100-regulator"); diff --git a/include/linux/regulator/pfuze100.h b/include/linux/regulator/pfuze100.h new file mode 100644 index 000000000000..65d550bf3954 --- /dev/null +++ b/include/linux/regulator/pfuze100.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef __LINUX_REG_PFUZE100_H +#define __LINUX_REG_PFUZE100_H + +#define PFUZE100_SW1AB 0 +#define PFUZE100_SW1C 1 +#define PFUZE100_SW2 2 +#define PFUZE100_SW3A 3 +#define PFUZE100_SW3B 4 +#define PFUZE100_SW4 5 +#define PFUZE100_SWBST 6 +#define PFUZE100_VSNVS 7 +#define PFUZE100_VREFDDR 8 +#define PFUZE100_VGEN1 9 +#define PFUZE100_VGEN2 10 +#define PFUZE100_VGEN3 11 +#define PFUZE100_VGEN4 12 +#define PFUZE100_VGEN5 13 +#define PFUZE100_VGEN6 14 +#define PFUZE100_MAX_REGULATOR 15 + +struct regulator_init_data; + +struct pfuze_regulator_platform_data { + struct regulator_init_data *init_data[PFUZE100_MAX_REGULATOR]; +}; + +#endif /* __LINUX_REG_PFUZE100_H */ -- cgit v1.2.3-71-gd317 From 36019265cf81cdb2d366b28016bb00b936efc751 Mon Sep 17 00:00:00 2001 From: Yacine Belkadi Date: Wed, 24 Jul 2013 23:11:53 -0700 Subject: ktime: fix some scripts/kernel-doc warnings When building the htmldocs (in verbose mode), scripts/kernel-doc reports the following type of warnings: Warning(include/linux/ktime.h:75): No description found for return value of 'ktime_set' Fix them by using a "Return:" section to describe the return values. (Also apply some minor reformatting along the way.) Signed-off-by: Yacine Belkadi Acked-by: Rob Landley Signed-off-by: Jiri Kosina --- include/linux/ktime.h | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/ktime.h b/include/linux/ktime.h index debf208b7611..31c0cd1c941a 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -69,7 +69,7 @@ typedef union ktime ktime_t; /* Kill this */ * @secs: seconds to set * @nsecs: nanoseconds to set * - * Return the ktime_t representation of the value + * Return: The ktime_t representation of the value. */ static inline ktime_t ktime_set(const long secs, const unsigned long nsecs) { @@ -151,7 +151,7 @@ static inline ktime_t ktime_set(const long secs, const unsigned long nsecs) * @lhs: minuend * @rhs: subtrahend * - * Returns the remainder of the subtraction + * Return: The remainder of the subtraction. */ static inline ktime_t ktime_sub(const ktime_t lhs, const ktime_t rhs) { @@ -169,7 +169,7 @@ static inline ktime_t ktime_sub(const ktime_t lhs, const ktime_t rhs) * @add1: addend1 * @add2: addend2 * - * Returns the sum of @add1 and @add2. + * Return: The sum of @add1 and @add2. */ static inline ktime_t ktime_add(const ktime_t add1, const ktime_t add2) { @@ -195,7 +195,7 @@ static inline ktime_t ktime_add(const ktime_t add1, const ktime_t add2) * @kt: addend * @nsec: the scalar nsec value to add * - * Returns the sum of @kt and @nsec in ktime_t format + * Return: The sum of @kt and @nsec in ktime_t format. */ extern ktime_t ktime_add_ns(const ktime_t kt, u64 nsec); @@ -204,7 +204,7 @@ extern ktime_t ktime_add_ns(const ktime_t kt, u64 nsec); * @kt: minuend * @nsec: the scalar nsec value to subtract * - * Returns the subtraction of @nsec from @kt in ktime_t format + * Return: The subtraction of @nsec from @kt in ktime_t format. */ extern ktime_t ktime_sub_ns(const ktime_t kt, u64 nsec); @@ -212,7 +212,7 @@ extern ktime_t ktime_sub_ns(const ktime_t kt, u64 nsec); * timespec_to_ktime - convert a timespec to ktime_t format * @ts: the timespec variable to convert * - * Returns a ktime_t variable with the converted timespec value + * Return: A ktime_t variable with the converted timespec value. */ static inline ktime_t timespec_to_ktime(const struct timespec ts) { @@ -224,7 +224,7 @@ static inline ktime_t timespec_to_ktime(const struct timespec ts) * timeval_to_ktime - convert a timeval to ktime_t format * @tv: the timeval variable to convert * - * Returns a ktime_t variable with the converted timeval value + * Return: A ktime_t variable with the converted timeval value. */ static inline ktime_t timeval_to_ktime(const struct timeval tv) { @@ -237,7 +237,7 @@ static inline ktime_t timeval_to_ktime(const struct timeval tv) * ktime_to_timespec - convert a ktime_t variable to timespec format * @kt: the ktime_t variable to convert * - * Returns the timespec representation of the ktime value + * Return: The timespec representation of the ktime value. */ static inline struct timespec ktime_to_timespec(const ktime_t kt) { @@ -249,7 +249,7 @@ static inline struct timespec ktime_to_timespec(const ktime_t kt) * ktime_to_timeval - convert a ktime_t variable to timeval format * @kt: the ktime_t variable to convert * - * Returns the timeval representation of the ktime value + * Return: The timeval representation of the ktime value. */ static inline struct timeval ktime_to_timeval(const ktime_t kt) { @@ -262,7 +262,7 @@ static inline struct timeval ktime_to_timeval(const ktime_t kt) * ktime_to_ns - convert a ktime_t variable to scalar nanoseconds * @kt: the ktime_t variable to convert * - * Returns the scalar nanoseconds representation of @kt + * Return: The scalar nanoseconds representation of @kt. */ static inline s64 ktime_to_ns(const ktime_t kt) { @@ -276,7 +276,9 @@ static inline s64 ktime_to_ns(const ktime_t kt) * @cmp1: comparable1 * @cmp2: comparable2 * - * Compare two ktime_t variables, returns 1 if equal + * Compare two ktime_t variables. + * + * Return: 1 if equal. */ static inline int ktime_equal(const ktime_t cmp1, const ktime_t cmp2) { @@ -288,7 +290,7 @@ static inline int ktime_equal(const ktime_t cmp1, const ktime_t cmp2) * @cmp1: comparable1 * @cmp2: comparable2 * - * Returns ... + * Return: ... * cmp1 < cmp2: return <0 * cmp1 == cmp2: return 0 * cmp1 > cmp2: return >0 @@ -342,7 +344,7 @@ extern ktime_t ktime_add_safe(const ktime_t lhs, const ktime_t rhs); * @kt: the ktime_t variable to convert * @ts: the timespec variable to store the result in * - * Returns true if there was a successful conversion, false if kt was 0. + * Return: %true if there was a successful conversion, %false if kt was 0. */ static inline __must_check bool ktime_to_timespec_cond(const ktime_t kt, struct timespec *ts) -- cgit v1.2.3-71-gd317 From fe3078fa5c367186c94a6652581ffbe9ccea4640 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 24 Jul 2013 21:06:15 +0200 Subject: drm: add unified vma offset manager If we want to map GPU memory into user-space, we need to linearize the addresses to not confuse mm-core. Currently, GEM and TTM both implement their own offset-managers to assign a pgoff to each object for user-space CPU access. GEM uses a hash-table, TTM uses an rbtree. This patch provides a unified implementation that can be used to replace both. TTM allows partial mmaps with a given offset, so we cannot use hashtables as the start address may not be known at mmap time. Hence, we use the rbtree-implementation of TTM. We could easily update drm_mm to use an rbtree instead of a linked list for it's object list and thus drop the rbtree from the vma-manager. However, this would slow down drm_mm object allocation for all other use-cases (rbtree insertion) and add another 4-8 bytes to each mm node. Hence, use the separate tree but allow for later migration. This is a rewrite of the 2012-proposal by David Airlie v2: - fix Docbook integration - drop drm_mm_node_linked() and use drm_mm_node_allocated() - remove unjustified likely/unlikely usage (but keep for rbtree paths) - remove BUG_ON() as drm_mm already does that - clarify page-based vs. byte-based addresses - use drm_vma_node_reset() for initialization, too v4: - allow external locking via drm_vma_offset_un/lock_lookup() - add locked lookup helper drm_vma_offset_lookup_locked() v5: - fix drm_vma_offset_lookup() to correctly validate range-mismatches (fix (offset > start + pages)) - fix drm_vma_offset_exact_lookup() to actually do what it says - remove redundant vm_pages member (add drm_vma_node_size() helper) - remove unneeded goto - fix documentation Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 6 + drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_vma_manager.c | 281 ++++++++++++++++++++++++++++++++++++++ include/drm/drm_vma_manager.h | 202 +++++++++++++++++++++++++++ 4 files changed, 490 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_vma_manager.c create mode 100644 include/drm/drm_vma_manager.h (limited to 'include') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 7d1278e7a434..87e22ecd9281 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2212,6 +2212,12 @@ void intel_crt_init(struct drm_device *dev) !Iinclude/drm/drm_rect.h !Edrivers/gpu/drm/drm_rect.c + + VMA Offset Manager +!Pdrivers/gpu/drm/drm_vma_manager.c vma offset manager +!Edrivers/gpu/drm/drm_vma_manager.c +!Iinclude/drm/drm_vma_manager.h + diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 801bcafa3028..d943b94afc90 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -13,7 +13,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_crtc.o drm_modes.o drm_edid.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ drm_trace_points.o drm_global.o drm_prime.o \ - drm_rect.o + drm_rect.o drm_vma_manager.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c new file mode 100644 index 000000000000..b966cea95f11 --- /dev/null +++ b/drivers/gpu/drm/drm_vma_manager.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA + * Copyright (c) 2012 David Airlie + * Copyright (c) 2013 David Herrmann + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * DOC: vma offset manager + * + * The vma-manager is responsible to map arbitrary driver-dependent memory + * regions into the linear user address-space. It provides offsets to the + * caller which can then be used on the address_space of the drm-device. It + * takes care to not overlap regions, size them appropriately and to not + * confuse mm-core by inconsistent fake vm_pgoff fields. + * Drivers shouldn't use this for object placement in VMEM. This manager should + * only be used to manage mappings into linear user-space VMs. + * + * We use drm_mm as backend to manage object allocations. But it is highly + * optimized for alloc/free calls, not lookups. Hence, we use an rb-tree to + * speed up offset lookups. + * + * You must not use multiple offset managers on a single address_space. + * Otherwise, mm-core will be unable to tear down memory mappings as the VM will + * no longer be linear. Please use VM_NONLINEAR in that case and implement your + * own offset managers. + * + * This offset manager works on page-based addresses. That is, every argument + * and return code (with the exception of drm_vma_node_offset_addr()) is given + * in number of pages, not number of bytes. That means, object sizes and offsets + * must always be page-aligned (as usual). + * If you want to get a valid byte-based user-space address for a given offset, + * please see drm_vma_node_offset_addr(). + */ + +/** + * drm_vma_offset_manager_init - Initialize new offset-manager + * @mgr: Manager object + * @page_offset: Offset of available memory area (page-based) + * @size: Size of available address space range (page-based) + * + * Initialize a new offset-manager. The offset and area size available for the + * manager are given as @page_offset and @size. Both are interpreted as + * page-numbers, not bytes. + * + * Adding/removing nodes from the manager is locked internally and protected + * against concurrent access. However, node allocation and destruction is left + * for the caller. While calling into the vma-manager, a given node must + * always be guaranteed to be referenced. + */ +void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr, + unsigned long page_offset, unsigned long size) +{ + rwlock_init(&mgr->vm_lock); + mgr->vm_addr_space_rb = RB_ROOT; + drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size); +} +EXPORT_SYMBOL(drm_vma_offset_manager_init); + +/** + * drm_vma_offset_manager_destroy() - Destroy offset manager + * @mgr: Manager object + * + * Destroy an object manager which was previously created via + * drm_vma_offset_manager_init(). The caller must remove all allocated nodes + * before destroying the manager. Otherwise, drm_mm will refuse to free the + * requested resources. + * + * The manager must not be accessed after this function is called. + */ +void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr) +{ + /* take the lock to protect against buggy drivers */ + write_lock(&mgr->vm_lock); + drm_mm_takedown(&mgr->vm_addr_space_mm); + write_unlock(&mgr->vm_lock); +} +EXPORT_SYMBOL(drm_vma_offset_manager_destroy); + +/** + * drm_vma_offset_lookup() - Find node in offset space + * @mgr: Manager object + * @start: Start address for object (page-based) + * @pages: Size of object (page-based) + * + * Find a node given a start address and object size. This returns the _best_ + * match for the given node. That is, @start may point somewhere into a valid + * region and the given node will be returned, as long as the node spans the + * whole requested area (given the size in number of pages as @pages). + * + * RETURNS: + * Returns NULL if no suitable node can be found. Otherwise, the best match + * is returned. It's the caller's responsibility to make sure the node doesn't + * get destroyed before the caller can access it. + */ +struct drm_vma_offset_node *drm_vma_offset_lookup(struct drm_vma_offset_manager *mgr, + unsigned long start, + unsigned long pages) +{ + struct drm_vma_offset_node *node; + + read_lock(&mgr->vm_lock); + node = drm_vma_offset_lookup_locked(mgr, start, pages); + read_unlock(&mgr->vm_lock); + + return node; +} +EXPORT_SYMBOL(drm_vma_offset_lookup); + +/** + * drm_vma_offset_lookup_locked() - Find node in offset space + * @mgr: Manager object + * @start: Start address for object (page-based) + * @pages: Size of object (page-based) + * + * Same as drm_vma_offset_lookup() but requires the caller to lock offset lookup + * manually. See drm_vma_offset_lock_lookup() for an example. + * + * RETURNS: + * Returns NULL if no suitable node can be found. Otherwise, the best match + * is returned. + */ +struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr, + unsigned long start, + unsigned long pages) +{ + struct drm_vma_offset_node *node, *best; + struct rb_node *iter; + unsigned long offset; + + iter = mgr->vm_addr_space_rb.rb_node; + best = NULL; + + while (likely(iter)) { + node = rb_entry(iter, struct drm_vma_offset_node, vm_rb); + offset = node->vm_node.start; + if (start >= offset) { + iter = iter->rb_right; + best = node; + if (start == offset) + break; + } else { + iter = iter->rb_left; + } + } + + /* verify that the node spans the requested area */ + if (best) { + offset = best->vm_node.start + best->vm_node.size; + if (offset < start + pages) + best = NULL; + } + + return best; +} +EXPORT_SYMBOL(drm_vma_offset_lookup_locked); + +/* internal helper to link @node into the rb-tree */ +static void _drm_vma_offset_add_rb(struct drm_vma_offset_manager *mgr, + struct drm_vma_offset_node *node) +{ + struct rb_node **iter = &mgr->vm_addr_space_rb.rb_node; + struct rb_node *parent = NULL; + struct drm_vma_offset_node *iter_node; + + while (likely(*iter)) { + parent = *iter; + iter_node = rb_entry(*iter, struct drm_vma_offset_node, vm_rb); + + if (node->vm_node.start < iter_node->vm_node.start) + iter = &(*iter)->rb_left; + else if (node->vm_node.start > iter_node->vm_node.start) + iter = &(*iter)->rb_right; + else + BUG(); + } + + rb_link_node(&node->vm_rb, parent, iter); + rb_insert_color(&node->vm_rb, &mgr->vm_addr_space_rb); +} + +/** + * drm_vma_offset_add() - Add offset node to manager + * @mgr: Manager object + * @node: Node to be added + * @pages: Allocation size visible to user-space (in number of pages) + * + * Add a node to the offset-manager. If the node was already added, this does + * nothing and return 0. @pages is the size of the object given in number of + * pages. + * After this call succeeds, you can access the offset of the node until it + * is removed again. + * + * If this call fails, it is safe to retry the operation or call + * drm_vma_offset_remove(), anyway. However, no cleanup is required in that + * case. + * + * @pages is not required to be the same size as the underlying memory object + * that you want to map. It only limits the size that user-space can map into + * their address space. + * + * RETURNS: + * 0 on success, negative error code on failure. + */ +int drm_vma_offset_add(struct drm_vma_offset_manager *mgr, + struct drm_vma_offset_node *node, unsigned long pages) +{ + int ret; + + write_lock(&mgr->vm_lock); + + if (drm_mm_node_allocated(&node->vm_node)) { + ret = 0; + goto out_unlock; + } + + ret = drm_mm_insert_node_generic(&mgr->vm_addr_space_mm, + &node->vm_node, pages, 0, 0); + if (ret) + goto out_unlock; + + _drm_vma_offset_add_rb(mgr, node); + +out_unlock: + write_unlock(&mgr->vm_lock); + return ret; +} +EXPORT_SYMBOL(drm_vma_offset_add); + +/** + * drm_vma_offset_remove() - Remove offset node from manager + * @mgr: Manager object + * @node: Node to be removed + * + * Remove a node from the offset manager. If the node wasn't added before, this + * does nothing. After this call returns, the offset and size will be 0 until a + * new offset is allocated via drm_vma_offset_add() again. Helper functions like + * drm_vma_node_start() and drm_vma_node_offset_addr() will return 0 if no + * offset is allocated. + */ +void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr, + struct drm_vma_offset_node *node) +{ + write_lock(&mgr->vm_lock); + + if (drm_mm_node_allocated(&node->vm_node)) { + rb_erase(&node->vm_rb, &mgr->vm_addr_space_rb); + drm_mm_remove_node(&node->vm_node); + memset(&node->vm_node, 0, sizeof(node->vm_node)); + } + + write_unlock(&mgr->vm_lock); +} +EXPORT_SYMBOL(drm_vma_offset_remove); diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h new file mode 100644 index 000000000000..7ee8c4babeb9 --- /dev/null +++ b/include/drm/drm_vma_manager.h @@ -0,0 +1,202 @@ +#ifndef __DRM_VMA_MANAGER_H__ +#define __DRM_VMA_MANAGER_H__ + +/* + * Copyright (c) 2013 David Herrmann + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +struct drm_vma_offset_node { + struct drm_mm_node vm_node; + struct rb_node vm_rb; +}; + +struct drm_vma_offset_manager { + rwlock_t vm_lock; + struct rb_root vm_addr_space_rb; + struct drm_mm vm_addr_space_mm; +}; + +void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr, + unsigned long page_offset, unsigned long size); +void drm_vma_offset_manager_destroy(struct drm_vma_offset_manager *mgr); + +struct drm_vma_offset_node *drm_vma_offset_lookup(struct drm_vma_offset_manager *mgr, + unsigned long start, + unsigned long pages); +struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_manager *mgr, + unsigned long start, + unsigned long pages); +int drm_vma_offset_add(struct drm_vma_offset_manager *mgr, + struct drm_vma_offset_node *node, unsigned long pages); +void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr, + struct drm_vma_offset_node *node); + +/** + * drm_vma_offset_exact_lookup() - Look up node by exact address + * @mgr: Manager object + * @start: Start address (page-based, not byte-based) + * @pages: Size of object (page-based) + * + * Same as drm_vma_offset_lookup() but does not allow any offset into the node. + * It only returns the exact object with the given start address. + * + * RETURNS: + * Node at exact start address @start. + */ +static inline struct drm_vma_offset_node * +drm_vma_offset_exact_lookup(struct drm_vma_offset_manager *mgr, + unsigned long start, + unsigned long pages) +{ + struct drm_vma_offset_node *node; + + node = drm_vma_offset_lookup(mgr, start, pages); + return (node && node->vm_node.start == start) ? node : NULL; +} + +/** + * drm_vma_offset_lock_lookup() - Lock lookup for extended private use + * @mgr: Manager object + * + * Lock VMA manager for extended lookups. Only *_locked() VMA function calls + * are allowed while holding this lock. All other contexts are blocked from VMA + * until the lock is released via drm_vma_offset_unlock_lookup(). + * + * Use this if you need to take a reference to the objects returned by + * drm_vma_offset_lookup_locked() before releasing this lock again. + * + * This lock must not be used for anything else than extended lookups. You must + * not call any other VMA helpers while holding this lock. + * + * Note: You're in atomic-context while holding this lock! + * + * Example: + * drm_vma_offset_lock_lookup(mgr); + * node = drm_vma_offset_lookup_locked(mgr); + * if (node) + * kref_get_unless_zero(container_of(node, sth, entr)); + * drm_vma_offset_unlock_lookup(mgr); + */ +static inline void drm_vma_offset_lock_lookup(struct drm_vma_offset_manager *mgr) +{ + read_lock(&mgr->vm_lock); +} + +/** + * drm_vma_offset_unlock_lookup() - Unlock lookup for extended private use + * @mgr: Manager object + * + * Release lookup-lock. See drm_vma_offset_lock_lookup() for more information. + */ +static inline void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *mgr) +{ + read_unlock(&mgr->vm_lock); +} + +/** + * drm_vma_node_reset() - Initialize or reset node object + * @node: Node to initialize or reset + * + * Reset a node to its initial state. This must be called if @node isn't + * already cleared (eg., via kzalloc) before using it with any VMA offset + * manager. + * + * This must not be called on an already allocated node, or you will leak + * memory. + */ +static inline void drm_vma_node_reset(struct drm_vma_offset_node *node) +{ + memset(node, 0, sizeof(*node)); +} + +/** + * drm_vma_node_start() - Return start address for page-based addressing + * @node: Node to inspect + * + * Return the start address of the given node. This can be used as offset into + * the linear VM space that is provided by the VMA offset manager. Note that + * this can only be used for page-based addressing. If you need a proper offset + * for user-space mappings, you must apply "<< PAGE_SHIFT" or use the + * drm_vma_node_offset_addr() helper instead. + * + * RETURNS: + * Start address of @node for page-based addressing. 0 if the node does not + * have an offset allocated. + */ +static inline unsigned long drm_vma_node_start(struct drm_vma_offset_node *node) +{ + return node->vm_node.start; +} + +/** + * drm_vma_node_size() - Return size (page-based) + * @node: Node to inspect + * + * Return the size as number of pages for the given node. This is the same size + * that was passed to drm_vma_offset_add(). If no offset is allocated for the + * node, this is 0. + * + * RETURNS: + * Size of @node as number of pages. 0 if the node does not have an offset + * allocated. + */ +static inline unsigned long drm_vma_node_size(struct drm_vma_offset_node *node) +{ + return node->vm_node.size; +} + +/** + * drm_vma_node_has_offset() - Check whether node is added to offset manager + * @node: Node to be checked + * + * RETURNS: + * true iff the node was previously allocated an offset and added to + * an vma offset manager. + */ +static inline bool drm_vma_node_has_offset(struct drm_vma_offset_node *node) +{ + return drm_mm_node_allocated(&node->vm_node); +} + +/** + * drm_vma_node_offset_addr() - Return sanitized offset for user-space mmaps + * @node: Linked offset node + * + * Same as drm_vma_node_start() but returns the address as a valid offset that + * can be used for user-space mappings during mmap(). + * This must not be called on unlinked nodes. + * + * RETURNS: + * Offset of @node for byte-based addressing. 0 if the node does not have an + * object allocated. + */ +static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node) +{ + return ((__u64)node->vm_node.start) << PAGE_SHIFT; +} + +#endif /* __DRM_VMA_MANAGER_H__ */ -- cgit v1.2.3-71-gd317 From 0de23977cfeb5b357ec884ba15417ae118ff9e9b Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 24 Jul 2013 21:07:52 +0200 Subject: drm/gem: convert to new unified vma manager Use the new vma manager instead of the old hashtable. Also convert all drivers to use the new convenience helpers. This drops all the (map_list.hash.key << PAGE_SHIFT) non-sense. Locking and access-management is exactly the same as before with an additional lock inside of the vma-manager, which strictly wouldn't be needed for gem. v2: - rebase on drm-next - init nodes via drm_vma_node_reset() in drm_gem.c v3: - fix tegra v4: - remove duplicate if (drm_vma_node_has_offset()) checks - inline now trivial drm_vma_node_offset_addr() calls v5: - skip node-reset on gem-init due to kzalloc() - do not allow mapping gem-objects with offsets (backwards compat) - remove unneccessary casts Cc: Inki Dae Cc: Rob Clark Cc: Dave Airlie Cc: Thierry Reding Signed-off-by: David Herrmann Acked-by: Patrik Jakobsson Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 89 +++++------------------------- drivers/gpu/drm/drm_gem_cma_helper.c | 16 ++---- drivers/gpu/drm/exynos/exynos_drm_gem.c | 14 ++--- drivers/gpu/drm/gma500/gem.c | 15 ++--- drivers/gpu/drm/i915/i915_gem.c | 10 ++-- drivers/gpu/drm/omapdrm/omap_gem.c | 28 +++++----- drivers/gpu/drm/omapdrm/omap_gem_helpers.c | 49 +--------------- drivers/gpu/drm/udl/udl_gem.c | 13 ++--- drivers/gpu/host1x/drm/gem.c | 5 +- include/drm/drmP.h | 7 +-- include/uapi/drm/drm.h | 2 +- 11 files changed, 62 insertions(+), 186 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 1ad9e7ec0119..3613b50b5c26 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -37,6 +37,7 @@ #include #include #include +#include /** @file drm_gem.c * @@ -102,14 +103,9 @@ drm_gem_init(struct drm_device *dev) } dev->mm_private = mm; - - if (drm_ht_create(&mm->offset_hash, 12)) { - kfree(mm); - return -ENOMEM; - } - - drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START, - DRM_FILE_PAGE_OFFSET_SIZE); + drm_vma_offset_manager_init(&mm->vma_manager, + DRM_FILE_PAGE_OFFSET_START, + DRM_FILE_PAGE_OFFSET_SIZE); return 0; } @@ -119,8 +115,7 @@ drm_gem_destroy(struct drm_device *dev) { struct drm_gem_mm *mm = dev->mm_private; - drm_mm_takedown(&mm->offset_manager); - drm_ht_remove(&mm->offset_hash); + drm_vma_offset_manager_destroy(&mm->vma_manager); kfree(mm); dev->mm_private = NULL; } @@ -302,12 +297,8 @@ drm_gem_free_mmap_offset(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; struct drm_gem_mm *mm = dev->mm_private; - struct drm_map_list *list = &obj->map_list; - drm_ht_remove_item(&mm->offset_hash, &list->hash); - drm_mm_put_block(list->file_offset_node); - kfree(list->map); - list->map = NULL; + drm_vma_offset_remove(&mm->vma_manager, &obj->vma_node); } EXPORT_SYMBOL(drm_gem_free_mmap_offset); @@ -327,54 +318,9 @@ drm_gem_create_mmap_offset(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; struct drm_gem_mm *mm = dev->mm_private; - struct drm_map_list *list; - struct drm_local_map *map; - int ret; - - /* Set the object up for mmap'ing */ - list = &obj->map_list; - list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL); - if (!list->map) - return -ENOMEM; - - map = list->map; - map->type = _DRM_GEM; - map->size = obj->size; - map->handle = obj; - - /* Get a DRM GEM mmap offset allocated... */ - list->file_offset_node = drm_mm_search_free(&mm->offset_manager, - obj->size / PAGE_SIZE, 0, false); - - if (!list->file_offset_node) { - DRM_ERROR("failed to allocate offset for bo %d\n", obj->name); - ret = -ENOSPC; - goto out_free_list; - } - list->file_offset_node = drm_mm_get_block(list->file_offset_node, - obj->size / PAGE_SIZE, 0); - if (!list->file_offset_node) { - ret = -ENOMEM; - goto out_free_list; - } - - list->hash.key = list->file_offset_node->start; - ret = drm_ht_insert_item(&mm->offset_hash, &list->hash); - if (ret) { - DRM_ERROR("failed to add to map hash\n"); - goto out_free_mm; - } - - return 0; - -out_free_mm: - drm_mm_put_block(list->file_offset_node); -out_free_list: - kfree(list->map); - list->map = NULL; - - return ret; + return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node, + obj->size / PAGE_SIZE); } EXPORT_SYMBOL(drm_gem_create_mmap_offset); @@ -703,8 +649,8 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) struct drm_file *priv = filp->private_data; struct drm_device *dev = priv->minor->dev; struct drm_gem_mm *mm = dev->mm_private; - struct drm_local_map *map = NULL; - struct drm_hash_item *hash; + struct drm_gem_object *obj; + struct drm_vma_offset_node *node; int ret = 0; if (drm_device_is_unplugged(dev)) @@ -712,21 +658,16 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) mutex_lock(&dev->struct_mutex); - if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) { + node = drm_vma_offset_exact_lookup(&mm->vma_manager, vma->vm_pgoff, + vma_pages(vma)); + if (!node) { mutex_unlock(&dev->struct_mutex); return drm_mmap(filp, vma); } - map = drm_hash_entry(hash, struct drm_map_list, hash)->map; - if (!map || - ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) { - ret = -EPERM; - goto out_unlock; - } - - ret = drm_gem_mmap_obj(map->handle, map->size, vma); + obj = container_of(node, struct drm_gem_object, vma_node); + ret = drm_gem_mmap_obj(obj, drm_vma_node_size(node), vma); -out_unlock: mutex_unlock(&dev->struct_mutex); return ret; diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index ece72a8ac245..847f09117666 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -27,11 +27,7 @@ #include #include #include - -static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj) -{ - return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT; -} +#include /* * __drm_gem_cma_create - Create a GEM CMA object without allocating memory @@ -172,8 +168,7 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj) { struct drm_gem_cma_object *cma_obj; - if (gem_obj->map_list.map) - drm_gem_free_mmap_offset(gem_obj); + drm_gem_free_mmap_offset(gem_obj); cma_obj = to_drm_gem_cma_obj(gem_obj); @@ -237,7 +232,7 @@ int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv, return -EINVAL; } - *offset = get_gem_mmap_offset(gem_obj); + *offset = drm_vma_node_offset_addr(&gem_obj->vma_node); drm_gem_object_unreference(gem_obj); @@ -301,12 +296,11 @@ void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m { struct drm_gem_object *obj = &cma_obj->base; struct drm_device *dev = obj->dev; - uint64_t off = 0; + uint64_t off; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - if (obj->map_list.map) - off = (uint64_t)obj->map_list.hash.key; + off = drm_vma_node_start(&obj->vma_node); seq_printf(m, "%2d (%2d) %08llx %08Zx %p %d", obj->name, obj->refcount.refcount.counter, diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 24c22a8c3364..be32db1ab290 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -152,8 +153,7 @@ out: exynos_drm_fini_buf(obj->dev, buf); exynos_gem_obj->buffer = NULL; - if (obj->map_list.map) - drm_gem_free_mmap_offset(obj); + drm_gem_free_mmap_offset(obj); /* release file pointer to gem object. */ drm_gem_object_release(obj); @@ -703,13 +703,11 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, goto unlock; } - if (!obj->map_list.map) { - ret = drm_gem_create_mmap_offset(obj); - if (ret) - goto out; - } + ret = drm_gem_create_mmap_offset(obj); + if (ret) + goto out; - *offset = (u64)obj->map_list.hash.key << PAGE_SHIFT; + *offset = drm_vma_node_offset_addr(&obj->vma_node); DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset); out: diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c index fe1d3320ce6a..2f77bea30b11 100644 --- a/drivers/gpu/drm/gma500/gem.c +++ b/drivers/gpu/drm/gma500/gem.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "psb_drv.h" int psb_gem_init_object(struct drm_gem_object *obj) @@ -38,8 +39,7 @@ void psb_gem_free_object(struct drm_gem_object *obj) struct gtt_range *gtt = container_of(obj, struct gtt_range, gem); /* Remove the list map if one is present */ - if (obj->map_list.map) - drm_gem_free_mmap_offset(obj); + drm_gem_free_mmap_offset(obj); drm_gem_object_release(obj); /* This must occur last as it frees up the memory of the GEM object */ @@ -81,13 +81,10 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev, /* What validation is needed here ? */ /* Make it mmapable */ - if (!obj->map_list.map) { - ret = drm_gem_create_mmap_offset(obj); - if (ret) - goto out; - } - /* GEM should really work out the hash offsets for us */ - *offset = (u64)obj->map_list.hash.key << PAGE_SHIFT; + ret = drm_gem_create_mmap_offset(obj); + if (ret) + goto out; + *offset = drm_vma_node_offset_addr(&obj->vma_node); out: drm_gem_object_unreference(obj); unlock: diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 46bf7e3887d4..53f81b3b3424 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -26,6 +26,7 @@ */ #include +#include #include #include "i915_drv.h" #include "i915_trace.h" @@ -1428,7 +1429,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj) if (obj->base.dev->dev_mapping) unmap_mapping_range(obj->base.dev->dev_mapping, - (loff_t)obj->base.map_list.hash.key<base.vma_node), obj->base.size, 1); obj->fault_mappable = false; @@ -1486,7 +1487,7 @@ static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj) struct drm_i915_private *dev_priv = obj->base.dev->dev_private; int ret; - if (obj->base.map_list.map) + if (drm_vma_node_has_offset(&obj->base.vma_node)) return 0; dev_priv->mm.shrinker_no_lock_stealing = true; @@ -1517,9 +1518,6 @@ out: static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj) { - if (!obj->base.map_list.map) - return; - drm_gem_free_mmap_offset(&obj->base); } @@ -1558,7 +1556,7 @@ i915_gem_mmap_gtt(struct drm_file *file, if (ret) goto out; - *offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT; + *offset = drm_vma_node_offset_addr(&obj->base.vma_node); out: drm_gem_object_unreference(&obj->base); diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index cbcd71e6ed83..f90531fc00c9 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -20,6 +20,7 @@ #include #include +#include #include "omap_drv.h" #include "omap_dmm_tiler.h" @@ -308,21 +309,20 @@ uint32_t omap_gem_flags(struct drm_gem_object *obj) static uint64_t mmap_offset(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; + int ret; + size_t size; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - if (!obj->map_list.map) { - /* Make it mmapable */ - size_t size = omap_gem_mmap_size(obj); - int ret = _drm_gem_create_mmap_offset_size(obj, size); - - if (ret) { - dev_err(dev->dev, "could not allocate mmap offset\n"); - return 0; - } + /* Make it mmapable */ + size = omap_gem_mmap_size(obj); + ret = _drm_gem_create_mmap_offset_size(obj, size); + if (ret) { + dev_err(dev->dev, "could not allocate mmap offset\n"); + return 0; } - return (uint64_t)obj->map_list.hash.key << PAGE_SHIFT; + return drm_vma_node_offset_addr(&obj->vma_node); } uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj) @@ -997,12 +997,11 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m) { struct drm_device *dev = obj->dev; struct omap_gem_object *omap_obj = to_omap_bo(obj); - uint64_t off = 0; + uint64_t off; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - if (obj->map_list.map) - off = (uint64_t)obj->map_list.hash.key; + off = drm_vma_node_start(&obj->vma_node); seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d", omap_obj->flags, obj->name, obj->refcount.refcount.counter, @@ -1309,8 +1308,7 @@ void omap_gem_free_object(struct drm_gem_object *obj) list_del(&omap_obj->mm_list); - if (obj->map_list.map) - drm_gem_free_mmap_offset(obj); + drm_gem_free_mmap_offset(obj); /* this means the object is still pinned.. which really should * not happen. I think.. diff --git a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c index f9eb679eb79b..dbb157542f8f 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem_helpers.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_helpers.c @@ -118,52 +118,7 @@ _drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size) { struct drm_device *dev = obj->dev; struct drm_gem_mm *mm = dev->mm_private; - struct drm_map_list *list; - struct drm_local_map *map; - int ret = 0; - - /* Set the object up for mmap'ing */ - list = &obj->map_list; - list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL); - if (!list->map) - return -ENOMEM; - - map = list->map; - map->type = _DRM_GEM; - map->size = size; - map->handle = obj; - - /* Get a DRM GEM mmap offset allocated... */ - list->file_offset_node = drm_mm_search_free(&mm->offset_manager, - size / PAGE_SIZE, 0, 0); - - if (!list->file_offset_node) { - DRM_ERROR("failed to allocate offset for bo %d\n", obj->name); - ret = -ENOSPC; - goto out_free_list; - } - - list->file_offset_node = drm_mm_get_block(list->file_offset_node, - size / PAGE_SIZE, 0); - if (!list->file_offset_node) { - ret = -ENOMEM; - goto out_free_list; - } - - list->hash.key = list->file_offset_node->start; - ret = drm_ht_insert_item(&mm->offset_hash, &list->hash); - if (ret) { - DRM_ERROR("failed to add to map hash\n"); - goto out_free_mm; - } - - return 0; - -out_free_mm: - drm_mm_put_block(list->file_offset_node); -out_free_list: - kfree(list->map); - list->map = NULL; - return ret; + return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node, + size / PAGE_SIZE); } diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index ef034fa3e6f5..2a4cb2f83b36 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -223,8 +223,7 @@ void udl_gem_free_object(struct drm_gem_object *gem_obj) if (obj->pages) udl_gem_put_pages(obj); - if (gem_obj->map_list.map) - drm_gem_free_mmap_offset(gem_obj); + drm_gem_free_mmap_offset(gem_obj); } /* the dumb interface doesn't work with the GEM straight MMAP @@ -247,13 +246,11 @@ int udl_gem_mmap(struct drm_file *file, struct drm_device *dev, ret = udl_gem_get_pages(gobj, GFP_KERNEL); if (ret) goto out; - if (!gobj->base.map_list.map) { - ret = drm_gem_create_mmap_offset(obj); - if (ret) - goto out; - } + ret = drm_gem_create_mmap_offset(obj); + if (ret) + goto out; - *offset = (u64)gobj->base.map_list.hash.key << PAGE_SHIFT; + *offset = drm_vma_node_offset_addr(&gobj->base.vma_node); out: drm_gem_object_unreference(&gobj->base); diff --git a/drivers/gpu/host1x/drm/gem.c b/drivers/gpu/host1x/drm/gem.c index c5e9a9b494c2..bc323b3dbe4d 100644 --- a/drivers/gpu/host1x/drm/gem.c +++ b/drivers/gpu/host1x/drm/gem.c @@ -108,7 +108,7 @@ static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo) unsigned int tegra_bo_get_mmap_offset(struct tegra_bo *bo) { - return (unsigned int)bo->gem.map_list.hash.key << PAGE_SHIFT; + return (unsigned int)drm_vma_node_offset_addr(&bo->gem.vma_node); } struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size) @@ -182,8 +182,7 @@ void tegra_bo_free_object(struct drm_gem_object *gem) { struct tegra_bo *bo = to_tegra_bo(gem); - if (gem->map_list.map) - drm_gem_free_mmap_offset(gem); + drm_gem_free_mmap_offset(gem); drm_gem_object_release(gem); tegra_bo_destroy(gem->dev, bo); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 0ab6a090a15c..4b518e05d293 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -71,6 +71,7 @@ #include #include #include +#include #include @@ -587,7 +588,6 @@ struct drm_map_list { struct drm_local_map *map; /**< mapping */ uint64_t user_token; struct drm_master *master; - struct drm_mm_node *file_offset_node; /**< fake offset */ }; /** @@ -622,8 +622,7 @@ struct drm_ati_pcigart_info { * GEM specific mm private for tracking GEM objects */ struct drm_gem_mm { - struct drm_mm offset_manager; /**< Offset mgmt for buffer objects */ - struct drm_open_hash offset_hash; /**< User token hash table for maps */ + struct drm_vma_offset_manager vma_manager; }; /** @@ -644,7 +643,7 @@ struct drm_gem_object { struct file *filp; /* Mapping info for this object */ - struct drm_map_list map_list; + struct drm_vma_offset_node vma_node; /** * Size of the object, in bytes. Immutable over the object's diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index 238a166b9fe6..272580ca320f 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -181,7 +181,7 @@ enum drm_map_type { _DRM_AGP = 3, /**< AGP/GART */ _DRM_SCATTER_GATHER = 4, /**< Scatter/gather memory for PCI DMA */ _DRM_CONSISTENT = 5, /**< Consistent memory for PCI DMA */ - _DRM_GEM = 6, /**< GEM object */ + _DRM_GEM = 6, /**< GEM object (obsolete) */ }; /** -- cgit v1.2.3-71-gd317 From 72525b3f333de54fa0c42ef87f27861e41478f1e Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 24 Jul 2013 21:08:53 +0200 Subject: drm/ttm: convert to unified vma offset manager Use the new vma-manager infrastructure. This doesn't change any implementation details as the vma-offset-manager is nearly copied 1-to-1 from TTM. The vm_lock is moved into the offset manager so we can drop it from TTM. During lookup, we use the vma locking helpers to take a reference to the found object. In all other scenarios, locking stays the same as before. We always guarantee that drm_vma_offset_remove() is called only during destruction. Hence, helpers like drm_vma_node_offset_addr() are always safe as long as the node has a valid offset. This also drops the addr_space_offset member as it is a copy of vm_start in vma_node objects. Use the accessor functions instead. v4: - remove vm_lock - use drm_vma_offset_lock_lookup() to protect lookup (instead of vm_lock) Cc: Dave Airlie Cc: Ben Skeggs Cc: Maarten Lankhorst Cc: Martin Peres Cc: Alex Deucher Cc: Thomas Hellstrom Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/ast/ast_main.c | 2 +- drivers/gpu/drm/cirrus/cirrus_main.c | 2 +- drivers/gpu/drm/mgag200/mgag200_main.c | 2 +- drivers/gpu/drm/nouveau/nouveau_display.c | 2 +- drivers/gpu/drm/nouveau/nouveau_gem.c | 2 +- drivers/gpu/drm/qxl/qxl_object.h | 2 +- drivers/gpu/drm/qxl/qxl_release.c | 2 +- drivers/gpu/drm/radeon/radeon_object.h | 5 +- drivers/gpu/drm/ttm/ttm_bo.c | 89 ++++++------------------------- drivers/gpu/drm/ttm/ttm_bo_util.c | 3 +- drivers/gpu/drm/ttm/ttm_bo_vm.c | 81 +++++++++++----------------- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 4 +- include/drm/ttm/ttm_bo_api.h | 15 ++---- include/drm/ttm/ttm_bo_driver.h | 10 ++-- 14 files changed, 66 insertions(+), 155 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index f60fd7bd1183..c195dc2abc09 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -487,7 +487,7 @@ void ast_gem_free_object(struct drm_gem_object *obj) static inline u64 ast_bo_mmap_offset(struct ast_bo *bo) { - return bo->bo.addr_space_offset; + return drm_vma_node_offset_addr(&bo->bo.vma_node); } int ast_dumb_mmap_offset(struct drm_file *file, diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index 35cbae827771..3a7a0efe3675 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -294,7 +294,7 @@ void cirrus_gem_free_object(struct drm_gem_object *obj) static inline u64 cirrus_bo_mmap_offset(struct cirrus_bo *bo) { - return bo->bo.addr_space_offset; + return drm_vma_node_offset_addr(&bo->bo.vma_node); } int diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index 9fa5685baee0..1a75ea395b33 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -349,7 +349,7 @@ void mgag200_gem_free_object(struct drm_gem_object *obj) static inline u64 mgag200_bo_mmap_offset(struct mgag200_bo *bo) { - return bo->bo.addr_space_offset; + return drm_vma_node_offset_addr(&bo->bo.vma_node); } int diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 708b2d1c0037..7a8caa126db6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -697,7 +697,7 @@ nouveau_display_dumb_map_offset(struct drm_file *file_priv, gem = drm_gem_object_lookup(dev, file_priv, handle); if (gem) { struct nouveau_bo *bo = gem->driver_private; - *poffset = bo->bo.addr_space_offset; + *poffset = drm_vma_node_offset_addr(&bo->bo.vma_node); drm_gem_object_unreference_unlocked(gem); return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index e72d09c068a8..86597ebf8c98 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -226,7 +226,7 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem, } rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT; - rep->map_handle = nvbo->bo.addr_space_offset; + rep->map_handle = drm_vma_node_offset_addr(&nvbo->bo.vma_node); rep->tile_mode = nvbo->tile_mode; rep->tile_flags = nvbo->tile_flags; return 0; diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h index ee7ad79ce781..af10165adb0d 100644 --- a/drivers/gpu/drm/qxl/qxl_object.h +++ b/drivers/gpu/drm/qxl/qxl_object.h @@ -59,7 +59,7 @@ static inline unsigned long qxl_bo_size(struct qxl_bo *bo) static inline u64 qxl_bo_mmap_offset(struct qxl_bo *bo) { - return bo->tbo.addr_space_offset; + return drm_vma_node_offset_addr(&bo->tbo.vma_node); } static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type, diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c index b443d6751d5f..1a648e1da6a6 100644 --- a/drivers/gpu/drm/qxl/qxl_release.c +++ b/drivers/gpu/drm/qxl/qxl_release.c @@ -87,7 +87,7 @@ qxl_release_free(struct qxl_device *qdev, for (i = 0 ; i < release->bo_count; ++i) { QXL_INFO(qdev, "release %llx\n", - release->bos[i]->tbo.addr_space_offset + drm_vma_node_offset_addr(&release->bos[i]->tbo.vma_node) - DRM_FILE_OFFSET); qxl_fence_remove_release(&release->bos[i]->fence, release->id); qxl_bo_unref(&release->bos[i]); diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h index 49c82c480013..209b11150263 100644 --- a/drivers/gpu/drm/radeon/radeon_object.h +++ b/drivers/gpu/drm/radeon/radeon_object.h @@ -113,13 +113,10 @@ static inline unsigned radeon_bo_gpu_page_alignment(struct radeon_bo *bo) * @bo: radeon object for which we query the offset * * Returns mmap offset of the object. - * - * Note: addr_space_offset is constant after ttm bo init thus isn't protected - * by any lock. */ static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo) { - return bo->tbo.addr_space_offset; + return drm_vma_node_offset_addr(&bo->tbo.vma_node); } extern int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type, diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index cb9dd674670c..3dc08b612292 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -615,13 +615,7 @@ static void ttm_bo_release(struct kref *kref) struct ttm_bo_device *bdev = bo->bdev; struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type]; - write_lock(&bdev->vm_lock); - if (likely(bo->vm_node != NULL)) { - rb_erase(&bo->vm_rb, &bdev->addr_space_rb); - drm_mm_put_block(bo->vm_node); - bo->vm_node = NULL; - } - write_unlock(&bdev->vm_lock); + drm_vma_offset_remove(&bdev->vma_manager, &bo->vma_node); ttm_mem_io_lock(man, false); ttm_mem_io_free_vm(bo); ttm_mem_io_unlock(man); @@ -1129,6 +1123,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev, bo->resv = &bo->ttm_resv; reservation_object_init(bo->resv); atomic_inc(&bo->glob->bo_count); + drm_vma_node_reset(&bo->vma_node); ret = ttm_bo_check_placement(bo, placement); @@ -1424,10 +1419,7 @@ int ttm_bo_device_release(struct ttm_bo_device *bdev) TTM_DEBUG("Swap list was clean\n"); spin_unlock(&glob->lru_lock); - BUG_ON(!drm_mm_clean(&bdev->addr_space_mm)); - write_lock(&bdev->vm_lock); - drm_mm_takedown(&bdev->addr_space_mm); - write_unlock(&bdev->vm_lock); + drm_vma_offset_manager_destroy(&bdev->vma_manager); return ret; } @@ -1441,7 +1433,6 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev, { int ret = -EINVAL; - rwlock_init(&bdev->vm_lock); bdev->driver = driver; memset(bdev->man, 0, sizeof(bdev->man)); @@ -1454,9 +1445,8 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev, if (unlikely(ret != 0)) goto out_no_sys; - bdev->addr_space_rb = RB_ROOT; - drm_mm_init(&bdev->addr_space_mm, file_page_offset, 0x10000000); - + drm_vma_offset_manager_init(&bdev->vma_manager, file_page_offset, + 0x10000000); INIT_DELAYED_WORK(&bdev->wq, ttm_bo_delayed_workqueue); INIT_LIST_HEAD(&bdev->ddestroy); bdev->dev_mapping = NULL; @@ -1498,12 +1488,17 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; - loff_t offset = (loff_t) bo->addr_space_offset; - loff_t holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT; + loff_t offset, holelen; if (!bdev->dev_mapping) return; - unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1); + + if (drm_vma_node_has_offset(&bo->vma_node)) { + offset = (loff_t) drm_vma_node_offset_addr(&bo->vma_node); + holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT; + + unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1); + } ttm_mem_io_free_vm(bo); } @@ -1520,31 +1515,6 @@ void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo) EXPORT_SYMBOL(ttm_bo_unmap_virtual); -static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo) -{ - struct ttm_bo_device *bdev = bo->bdev; - struct rb_node **cur = &bdev->addr_space_rb.rb_node; - struct rb_node *parent = NULL; - struct ttm_buffer_object *cur_bo; - unsigned long offset = bo->vm_node->start; - unsigned long cur_offset; - - while (*cur) { - parent = *cur; - cur_bo = rb_entry(parent, struct ttm_buffer_object, vm_rb); - cur_offset = cur_bo->vm_node->start; - if (offset < cur_offset) - cur = &parent->rb_left; - else if (offset > cur_offset) - cur = &parent->rb_right; - else - BUG(); - } - - rb_link_node(&bo->vm_rb, parent, cur); - rb_insert_color(&bo->vm_rb, &bdev->addr_space_rb); -} - /** * ttm_bo_setup_vm: * @@ -1559,38 +1529,9 @@ static void ttm_bo_vm_insert_rb(struct ttm_buffer_object *bo) static int ttm_bo_setup_vm(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; - int ret; - -retry_pre_get: - ret = drm_mm_pre_get(&bdev->addr_space_mm); - if (unlikely(ret != 0)) - return ret; - - write_lock(&bdev->vm_lock); - bo->vm_node = drm_mm_search_free(&bdev->addr_space_mm, - bo->mem.num_pages, 0, 0); - - if (unlikely(bo->vm_node == NULL)) { - ret = -ENOMEM; - goto out_unlock; - } - bo->vm_node = drm_mm_get_block_atomic(bo->vm_node, - bo->mem.num_pages, 0); - - if (unlikely(bo->vm_node == NULL)) { - write_unlock(&bdev->vm_lock); - goto retry_pre_get; - } - - ttm_bo_vm_insert_rb(bo); - write_unlock(&bdev->vm_lock); - bo->addr_space_offset = ((uint64_t) bo->vm_node->start) << PAGE_SHIFT; - - return 0; -out_unlock: - write_unlock(&bdev->vm_lock); - return ret; + return drm_vma_offset_add(&bdev->vma_manager, &bo->vma_node, + bo->mem.num_pages); } int ttm_bo_wait(struct ttm_buffer_object *bo, diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 319cf4127c5b..7cc904d3a4d1 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -450,7 +451,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, INIT_LIST_HEAD(&fbo->lru); INIT_LIST_HEAD(&fbo->swap); INIT_LIST_HEAD(&fbo->io_reserve_lru); - fbo->vm_node = NULL; + drm_vma_node_reset(&fbo->vma_node); atomic_set(&fbo->cpu_writers, 0); spin_lock(&bdev->fence_lock); diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 3df9f16b041c..8c0e2c020215 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -40,37 +41,6 @@ #define TTM_BO_VM_NUM_PREFAULT 16 -static struct ttm_buffer_object *ttm_bo_vm_lookup_rb(struct ttm_bo_device *bdev, - unsigned long page_start, - unsigned long num_pages) -{ - struct rb_node *cur = bdev->addr_space_rb.rb_node; - unsigned long cur_offset; - struct ttm_buffer_object *bo; - struct ttm_buffer_object *best_bo = NULL; - - while (likely(cur != NULL)) { - bo = rb_entry(cur, struct ttm_buffer_object, vm_rb); - cur_offset = bo->vm_node->start; - if (page_start >= cur_offset) { - cur = cur->rb_right; - best_bo = bo; - if (page_start == cur_offset) - break; - } else - cur = cur->rb_left; - } - - if (unlikely(best_bo == NULL)) - return NULL; - - if (unlikely((best_bo->vm_node->start + best_bo->num_pages) < - (page_start + num_pages))) - return NULL; - - return best_bo; -} - static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct ttm_buffer_object *bo = (struct ttm_buffer_object *) @@ -146,9 +116,9 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) } page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) + - bo->vm_node->start - vma->vm_pgoff; + drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff; page_last = vma_pages(vma) + - bo->vm_node->start - vma->vm_pgoff; + drm_vma_node_start(&bo->vma_node) - vma->vm_pgoff; if (unlikely(page_offset >= bo->num_pages)) { retval = VM_FAULT_SIGBUS; @@ -249,6 +219,30 @@ static const struct vm_operations_struct ttm_bo_vm_ops = { .close = ttm_bo_vm_close }; +static struct ttm_buffer_object *ttm_bo_vm_lookup(struct ttm_bo_device *bdev, + unsigned long offset, + unsigned long pages) +{ + struct drm_vma_offset_node *node; + struct ttm_buffer_object *bo = NULL; + + drm_vma_offset_lock_lookup(&bdev->vma_manager); + + node = drm_vma_offset_lookup_locked(&bdev->vma_manager, offset, pages); + if (likely(node)) { + bo = container_of(node, struct ttm_buffer_object, vma_node); + if (!kref_get_unless_zero(&bo->kref)) + bo = NULL; + } + + drm_vma_offset_unlock_lookup(&bdev->vma_manager); + + if (!bo) + pr_err("Could not find buffer object to map\n"); + + return bo; +} + int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma, struct ttm_bo_device *bdev) { @@ -256,17 +250,9 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma, struct ttm_buffer_object *bo; int ret; - read_lock(&bdev->vm_lock); - bo = ttm_bo_vm_lookup_rb(bdev, vma->vm_pgoff, - vma_pages(vma)); - if (likely(bo != NULL) && !kref_get_unless_zero(&bo->kref)) - bo = NULL; - read_unlock(&bdev->vm_lock); - - if (unlikely(bo == NULL)) { - pr_err("Could not find buffer object to map\n"); + bo = ttm_bo_vm_lookup(bdev, vma->vm_pgoff, vma_pages(vma)); + if (unlikely(!bo)) return -EINVAL; - } driver = bo->bdev->driver; if (unlikely(!driver->verify_access)) { @@ -324,12 +310,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp, bool no_wait = false; bool dummy; - read_lock(&bdev->vm_lock); - bo = ttm_bo_vm_lookup_rb(bdev, dev_offset, 1); - if (likely(bo != NULL)) - ttm_bo_reference(bo); - read_unlock(&bdev->vm_lock); - + bo = ttm_bo_vm_lookup(bdev, dev_offset, 1); if (unlikely(bo == NULL)) return -EFAULT; @@ -343,7 +324,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp, if (unlikely(ret != 0)) goto out_unref; - kmap_offset = dev_offset - bo->vm_node->start; + kmap_offset = dev_offset - drm_vma_node_start(&bo->vma_node); if (unlikely(kmap_offset >= bo->num_pages)) { ret = -EFBIG; goto out_unref; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 7953d1f90b63..0e67cf41065d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -500,7 +500,7 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data, goto out_no_dmabuf; rep->handle = handle; - rep->map_handle = dma_buf->base.addr_space_offset; + rep->map_handle = drm_vma_node_offset_addr(&dma_buf->base.vma_node); rep->cur_gmr_id = handle; rep->cur_gmr_offset = 0; @@ -834,7 +834,7 @@ int vmw_dumb_map_offset(struct drm_file *file_priv, if (ret != 0) return -EINVAL; - *offset = out_buf->base.addr_space_offset; + *offset = drm_vma_node_offset_addr(&out_buf->base.vma_node); vmw_dmabuf_unreference(&out_buf); return 0; } diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index 8a6aa56ece52..751eaffbf0d5 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -32,12 +32,12 @@ #define _TTM_BO_API_H_ #include +#include #include #include #include #include #include -#include #include #include @@ -145,7 +145,6 @@ struct ttm_tt; * @type: The bo type. * @destroy: Destruction function. If NULL, kfree is used. * @num_pages: Actual number of pages. - * @addr_space_offset: Address space offset. * @acc_size: Accounted size for this object. * @kref: Reference count of this buffer object. When this refcount reaches * zero, the object is put on the delayed delete list. @@ -166,8 +165,7 @@ struct ttm_tt; * @swap: List head for swap LRU list. * @sync_obj: Pointer to a synchronization object. * @priv_flags: Flags describing buffer object internal state. - * @vm_rb: Rb node for the vm rb tree. - * @vm_node: Address space manager node. + * @vma_node: Address space manager node. * @offset: The current GPU offset, which can have different meanings * depending on the memory type. For SYSTEM type memory, it should be 0. * @cur_placement: Hint of current placement. @@ -194,7 +192,6 @@ struct ttm_buffer_object { enum ttm_bo_type type; void (*destroy) (struct ttm_buffer_object *); unsigned long num_pages; - uint64_t addr_space_offset; size_t acc_size; /** @@ -238,13 +235,7 @@ struct ttm_buffer_object { void *sync_obj; unsigned long priv_flags; - /** - * Members protected by the bdev::vm_lock - */ - - struct rb_node vm_rb; - struct drm_mm_node *vm_node; - + struct drm_vma_offset_node vma_node; /** * Special members that are protected by the reserve lock diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 984fc2d571a1..8639c85d61c4 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -519,7 +520,7 @@ struct ttm_bo_global { * @man: An array of mem_type_managers. * @fence_lock: Protects the synchronizing members on *all* bos belonging * to this device. - * @addr_space_mm: Range manager for the device address space. + * @vma_manager: Address space manager * lru_lock: Spinlock that protects the buffer+device lru lists and * ddestroy lists. * @val_seq: Current validation sequence. @@ -537,14 +538,13 @@ struct ttm_bo_device { struct list_head device_list; struct ttm_bo_global *glob; struct ttm_bo_driver *driver; - rwlock_t vm_lock; struct ttm_mem_type_manager man[TTM_NUM_MEM_TYPES]; spinlock_t fence_lock; + /* - * Protected by the vm lock. + * Protected by internal locks. */ - struct rb_root addr_space_rb; - struct drm_mm addr_space_mm; + struct drm_vma_offset_manager vma_manager; /* * Protected by the global:lru lock. -- cgit v1.2.3-71-gd317 From 51335df9f044ccfafb029f4d7fbeb11c4526340a Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 24 Jul 2013 21:10:03 +0200 Subject: drm/vma: provide drm_vma_node_unmap() helper Instead of unmapping the nodes in TTM and GEM users manually, we provide a generic wrapper which does the correct thing for all vma-nodes. v2: remove bdev->dev_mapping test in ttm_bo_unmap_virtual_unlocked() as ttm_mem_io_free_vm() does nothing in that case (io_reserved_vm is 0). v4: Fix docbook comments v5: use drm_vma_node_size() Cc: Dave Airlie Cc: Maarten Lankhorst Cc: Thomas Hellstrom Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 6 +----- drivers/gpu/drm/ttm/ttm_bo.c | 11 +---------- include/drm/drm_vma_manager.h | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 53f81b3b3424..8673a000a373 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1427,11 +1427,7 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj) if (!obj->fault_mappable) return; - if (obj->base.dev->dev_mapping) - unmap_mapping_range(obj->base.dev->dev_mapping, - (loff_t)drm_vma_node_offset_addr(&obj->base.vma_node), - obj->base.size, 1); - + drm_vma_node_unmap(&obj->base.vma_node, obj->base.dev->dev_mapping); obj->fault_mappable = false; } diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 3dc08b612292..050edfaf5b88 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1488,17 +1488,8 @@ bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; - loff_t offset, holelen; - if (!bdev->dev_mapping) - return; - - if (drm_vma_node_has_offset(&bo->vma_node)) { - offset = (loff_t) drm_vma_node_offset_addr(&bo->vma_node); - holelen = ((loff_t) bo->mem.num_pages) << PAGE_SHIFT; - - unmap_mapping_range(bdev->dev_mapping, offset, holelen, 1); - } + drm_vma_node_unmap(&bo->vma_node, bdev->dev_mapping); ttm_mem_io_free_vm(bo); } diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h index 7ee8c4babeb9..22eedac046ac 100644 --- a/include/drm/drm_vma_manager.h +++ b/include/drm/drm_vma_manager.h @@ -24,6 +24,7 @@ */ #include +#include #include #include #include @@ -199,4 +200,25 @@ static inline __u64 drm_vma_node_offset_addr(struct drm_vma_offset_node *node) return ((__u64)node->vm_node.start) << PAGE_SHIFT; } +/** + * drm_vma_node_unmap() - Unmap offset node + * @node: Offset node + * @file_mapping: Address space to unmap @node from + * + * Unmap all userspace mappings for a given offset node. The mappings must be + * associated with the @file_mapping address-space. If no offset exists or + * the address-space is invalid, nothing is done. + * + * This call is unlocked. The caller must guarantee that drm_vma_offset_remove() + * is not called on this node concurrently. + */ +static inline void drm_vma_node_unmap(struct drm_vma_offset_node *node, + struct address_space *file_mapping) +{ + if (file_mapping && drm_vma_node_has_offset(node)) + unmap_mapping_range(file_mapping, + drm_vma_node_offset_addr(node), + drm_vma_node_size(node) << PAGE_SHIFT, 1); +} + #endif /* __DRM_VMA_MANAGER_H__ */ -- cgit v1.2.3-71-gd317 From a77b15a60cb1d54d87e8794bfbc94c9ccee679ed Mon Sep 17 00:00:00 2001 From: Mikel Astiz Date: Fri, 28 Jun 2013 10:56:27 +0200 Subject: Bluetooth: Add HCI authentication capabilities macros Add macros for the HCI capabilities as described in the Bluetooth Core Specification v4.0, Volume 2, part E, section 7.1.29. Signed-off-by: Mikel Astiz Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 3c592cf473da..a01fbb4a0499 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -296,6 +296,12 @@ enum { #define HCI_AT_GENERAL_BONDING 0x04 #define HCI_AT_GENERAL_BONDING_MITM 0x05 +/* I/O capabilities */ +#define HCI_IO_DISPLAY_ONLY 0x00 +#define HCI_IO_DISPLAY_YESNO 0x01 +#define HCI_IO_KEYBOARD_ONLY 0x02 +#define HCI_IO_NO_INPUT_OUTPUT 0x03 + /* Link Key types */ #define HCI_LK_COMBINATION 0x00 #define HCI_LK_LOCAL_UNIT 0x01 -- cgit v1.2.3-71-gd317 From 928bea964827d7824b548c1f8e06eccbbc4d0d7d Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 22 Jul 2013 14:37:17 -0700 Subject: PCI: Delay enabling bridges until they're needed We currently enable PCI bridges after scanning a bus and assigning resources. This is often done in arch code. This patch changes this so we don't enable a bridge until necessary, i.e., until we enable a PCI device behind the bridge. We do this in the generic pci_enable_device() path, so this also removes the arch-specific code to enable bridges. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- arch/arm/kernel/bios32.c | 5 ----- arch/m68k/platform/coldfire/pci.c | 1 - arch/mips/pci/pci.c | 1 - arch/sh/drivers/pci/pci.c | 1 - drivers/acpi/pci_root.c | 3 --- drivers/parisc/lba_pci.c | 1 - drivers/pci/bus.c | 19 ------------------- drivers/pci/hotplug/acpiphp_glue.c | 1 - drivers/pci/pci.c | 20 ++++++++++++++++++++ drivers/pci/probe.c | 1 - drivers/pci/setup-bus.c | 10 +++------- drivers/pcmcia/cardbus.c | 1 - include/linux/pci.h | 1 - 13 files changed, 23 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 261fcc826169..88e14d74b6de 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -525,11 +525,6 @@ void pci_common_init_dev(struct device *parent, struct hw_pci *hw) * Assign resources. */ pci_bus_assign_resources(bus); - - /* - * Enable bridges - */ - pci_enable_bridges(bus); } /* diff --git a/arch/m68k/platform/coldfire/pci.c b/arch/m68k/platform/coldfire/pci.c index b33f97a13e6d..df9679238b6d 100644 --- a/arch/m68k/platform/coldfire/pci.c +++ b/arch/m68k/platform/coldfire/pci.c @@ -319,7 +319,6 @@ static int __init mcf_pci_init(void) pci_fixup_irqs(pci_common_swizzle, mcf_pci_map_irq); pci_bus_size_bridges(rootbus); pci_bus_assign_resources(rootbus); - pci_enable_bridges(rootbus); return 0; } diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index 594e60d6a43b..33e7aa52d9c4 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -113,7 +113,6 @@ static void pcibios_scanbus(struct pci_controller *hose) if (!pci_has_flag(PCI_PROBE_ONLY)) { pci_bus_size_bridges(bus); pci_bus_assign_resources(bus); - pci_enable_bridges(bus); } } } diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index 102f5d58b037..60ed3e1c4b75 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -69,7 +69,6 @@ static void pcibios_scanbus(struct pci_channel *hose) pci_bus_size_bridges(bus); pci_bus_assign_resources(bus); - pci_enable_bridges(bus); } else { pci_free_resource_list(&resources); } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 5917839321b8..faa1d29c0261 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -527,9 +527,6 @@ static int acpi_pci_root_add(struct acpi_device *device, if (system_state != SYSTEM_BOOTING) { pcibios_resource_survey_bus(root->bus); pci_assign_unassigned_bus_resources(root->bus); - - /* need to after hot-added ioapic is registered */ - pci_enable_bridges(root->bus); } pci_bus_add_devices(root->bus); diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index 19f6f70c67d3..37e71ff6408d 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -1590,7 +1590,6 @@ lba_driver_probe(struct parisc_device *dev) lba_dump_res(&lba_dev->hba.lmmio_space, 2); #endif } - pci_enable_bridges(lba_bus); /* ** Once PCI register ops has walked the bus, access to config diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index b1ff02ab4f13..fc1b74013743 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -216,24 +216,6 @@ void pci_bus_add_devices(const struct pci_bus *bus) } } -void pci_enable_bridges(struct pci_bus *bus) -{ - struct pci_dev *dev; - int retval; - - list_for_each_entry(dev, &bus->devices, bus_list) { - if (dev->subordinate) { - if (!pci_is_enabled(dev)) { - retval = pci_enable_device(dev); - if (retval) - dev_err(&dev->dev, "Error enabling bridge (%d), continuing\n", retval); - pci_set_master(dev); - } - pci_enable_bridges(dev->subordinate); - } - } -} - /** pci_walk_bus - walk devices on/under bus, calling callback. * @top bus whose devices should be walked * @cb callback to be called for each device found @@ -301,4 +283,3 @@ EXPORT_SYMBOL(pci_bus_put); EXPORT_SYMBOL(pci_bus_alloc_resource); EXPORT_SYMBOL_GPL(pci_bus_add_device); EXPORT_SYMBOL(pci_bus_add_devices); -EXPORT_SYMBOL(pci_enable_bridges); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 59df8575a48c..52dee9d31e1c 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -723,7 +723,6 @@ static int __ref enable_device(struct acpiphp_slot *slot) acpiphp_sanitize_bus(bus); acpiphp_set_hpp_values(bus); acpiphp_set_acpi_region(slot); - pci_enable_bridges(bus); list_for_each_entry(dev, &bus->devices, bus_list) { /* Assume that newly added devices are powered on already. */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e37fea6e178d..44a1a8a0ad7b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1145,6 +1145,24 @@ int pci_reenable_device(struct pci_dev *dev) return 0; } +static void pci_enable_bridge(struct pci_dev *dev) +{ + int retval; + + if (!dev) + return; + + pci_enable_bridge(dev->bus->self); + + if (pci_is_enabled(dev)) + return; + retval = pci_enable_device(dev); + if (retval) + dev_err(&dev->dev, "Error enabling bridge (%d), continuing\n", + retval); + pci_set_master(dev); +} + static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) { int err; @@ -1165,6 +1183,8 @@ static int pci_enable_device_flags(struct pci_dev *dev, unsigned long flags) if (atomic_inc_return(&dev->enable_cnt) > 1) return 0; /* already enabled */ + pci_enable_bridge(dev->bus->self); + /* only skip sriov related */ for (i = 0; i <= PCI_ROM_RESOURCE; i++) if (dev->resource[i].flags & flags) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 46ada5c098eb..85c114cd91cc 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1979,7 +1979,6 @@ unsigned int __ref pci_rescan_bus(struct pci_bus *bus) max = pci_scan_child_bus(bus); pci_assign_unassigned_bus_resources(bus); - pci_enable_bridges(bus); pci_bus_add_devices(bus); return max; diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 4d9ebb4ce015..8f86be13678f 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1440,7 +1440,7 @@ again: /* any device complain? */ if (list_empty(&fail_head)) - goto enable_and_dump; + goto dump; if (tried_times >= pci_try_num) { if (enable_local == undefined) @@ -1449,7 +1449,7 @@ again: dev_info(&bus->dev, "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n"); free_list(&fail_head); - goto enable_and_dump; + goto dump; } dev_printk(KERN_DEBUG, &bus->dev, @@ -1482,10 +1482,7 @@ again: goto again; -enable_and_dump: - /* Depth last, update the hardware. */ - pci_enable_bridges(bus); - +dump: /* dump the resource on buses */ pci_bus_dump_resources(bus); } @@ -1556,7 +1553,6 @@ enable_all: if (retval) dev_err(&bridge->dev, "Error reenabling bridge (%d)\n", retval); pci_set_master(bridge); - pci_enable_bridges(parent); } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index 9d3ac998fc1f..b2a98cdbd0d2 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -91,7 +91,6 @@ int __ref cb_alloc(struct pcmcia_socket *s) if (s->tune_bridge) s->tune_bridge(s, bus); - pci_enable_bridges(bus); pci_bus_add_devices(bus); return 0; diff --git a/include/linux/pci.h b/include/linux/pci.h index 0fd1f1582fa1..8cd1e6f30acd 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1043,7 +1043,6 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus, resource_size_t, resource_size_t), void *alignf_data); -void pci_enable_bridges(struct pci_bus *bus); /* Proper probing supporting hot-pluggable devices */ int __must_check __pci_register_driver(struct pci_driver *, struct module *, -- cgit v1.2.3-71-gd317 From 39772038ea93e85ea4f1307ec9c1f48a063d89a0 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Mon, 22 Jul 2013 14:37:18 -0700 Subject: PCI: Assign resources for hot-added host bridge more aggressively When hot-adding an ACPI host bridge, use pci_assign_unassigned_root_bus_resources() instead of pci_assign_unassigned_bus_resources(). The former is more aggressive and will release and reassign existing resources if necessary. This is safe at hot-add time because no drivers are bound to devices below the new host bridge yet. [bhelgaas: changelog, split __init changes out for reviewability] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/acpi/pci_root.c | 2 +- drivers/pci/setup-bus.c | 2 +- include/linux/pci.h | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index faa1d29c0261..ce04eb28e029 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -526,7 +526,7 @@ static int acpi_pci_root_add(struct acpi_device *device, if (system_state != SYSTEM_BOOTING) { pcibios_resource_survey_bus(root->bus); - pci_assign_unassigned_bus_resources(root->bus); + pci_assign_unassigned_root_bus_resources(root->bus); } pci_bus_add_devices(root->bus); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 8d1e654256a8..94b777d108bb 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1395,7 +1395,7 @@ static enum enable_type pci_realloc_detect(struct pci_bus *bus, * second and later try will clear small leaf bridge res * will stop till to the max deepth if can not find good one */ -static void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) +void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) { LIST_HEAD(realloc_head); /* list of resources that want additional resources */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 8cd1e6f30acd..e494c90a00d1 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1003,6 +1003,7 @@ int pci_claim_resource(struct pci_dev *, int); void pci_assign_unassigned_resources(void); void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge); void pci_assign_unassigned_bus_resources(struct pci_bus *bus); +void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus); void pdev_enable_device(struct pci_dev *); int pci_enable_resources(struct pci_dev *, int mask); void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *), -- cgit v1.2.3-71-gd317 From 42189d854f174e7b29e0fdb4da9984ba63126a92 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 2 Jul 2013 10:50:15 +0300 Subject: usb: clamp bInterval to allowed range bInterval must be within the range 1 - 16 when running at High/Super speed, and within the range 1 - 255 when running at Full/Low speed. In order to catch drivers passing a too large bInterval on Super/High speed scenarios (thus overflowing urb->interval), let's clamp() the argument to the allowed ranges. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- include/linux/usb.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/usb.h b/include/linux/usb.h index e99b2a114af4..eb4252a0da7e 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1547,10 +1547,16 @@ static inline void usb_fill_int_urb(struct urb *urb, urb->transfer_buffer_length = buffer_length; urb->complete = complete_fn; urb->context = context; - if (dev->speed == USB_SPEED_HIGH || dev->speed == USB_SPEED_SUPER) + + if (dev->speed == USB_SPEED_HIGH || dev->speed == USB_SPEED_SUPER) { + /* make sure interval is within allowed range */ + interval = clamp(interval, 1, 16); + urb->interval = 1 << (interval - 1); - else + } else { urb->interval = interval; + } + urb->start_frame = -1; } -- cgit v1.2.3-71-gd317 From 323f54ed0f3ce20e9946c961fc928ccdb80d9345 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 25 Jul 2013 14:26:10 -0400 Subject: numa: Mark __node_set() as __always_inline It is posible for some compilers to decide that __node_set() does not need to be made turned into an inline function. When the compiler does this on an __init function calling it on __initdata we get a section mismatch warning now. Use __always_inline to ensure that we will be inlined. Reported-by: Paul Bolle Cc: Jianpeng Ma Cc: Rusty Russell Cc: Lai Jiangshan Cc: Yasuaki Ishimatsu Cc: Wen Congyang Cc: Jiang Liu Cc: KOSAKI Motohiro Cc: Minchan Kim Cc: Mel Gorman Cc: David Rientjes Cc: Yinghai Lu Cc: Greg KH Signed-off-by: Tom Rini Link: http://lkml.kernel.org/r/1374776770-32361-1-git-send-email-trini@ti.com Signed-off-by: Ingo Molnar --- include/linux/nodemask.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/nodemask.h b/include/linux/nodemask.h index 4e2cbfa640b7..58b9a02c38d2 100644 --- a/include/linux/nodemask.h +++ b/include/linux/nodemask.h @@ -98,8 +98,17 @@ typedef struct { DECLARE_BITMAP(bits, MAX_NUMNODES); } nodemask_t; extern nodemask_t _unused_nodemask_arg_; +/* + * The inline keyword gives the compiler room to decide to inline, or + * not inline a function as it sees best. However, as these functions + * are called in both __init and non-__init functions, if they are not + * inlined we will end up with a section mis-match error (of the type of + * freeable items not being freed). So we must use __always_inline here + * to fix the problem. If other functions in the future also end up in + * this situation they will also need to be annotated as __always_inline + */ #define node_set(node, dst) __node_set((node), &(dst)) -static inline void __node_set(int node, volatile nodemask_t *dstp) +static __always_inline void __node_set(int node, volatile nodemask_t *dstp) { set_bit(node, dstp->bits); } -- cgit v1.2.3-71-gd317 From c817a67ecba7c3c2aaa104796d78f160af60920d Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 27 Jun 2013 15:06:14 +0100 Subject: kobject: delayed kobject release: help find buggy drivers Implement debugging for kobject release functions. kobjects are reference counted, so the drop of the last reference to them is not predictable. However, the common case is for the last reference to be the kobject's removal from a subsystem, which results in the release function being immediately called. This can hide subtle bugs, which can occur when another thread holds a reference to the kobject at the same time that a kobject is removed. This results in the release method being delayed. In order to make these kinds of problems more visible, the following patch implements a delayed release; this has the effect that the release function will be out of order with respect to the removal of the kobject in the same manner that it would be if a reference was being held. This provides us with an easy way to allow driver writers to debug their drivers and fix otherwise hidden problems. Signed-off-by: Russell King Signed-off-by: Greg Kroah-Hartman --- include/linux/kobject.h | 4 ++++ lib/Kconfig.debug | 19 +++++++++++++++++++ lib/kobject.c | 22 +++++++++++++++++++--- 3 files changed, 42 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 939b11268c86..de6dcbcc6ef7 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -26,6 +26,7 @@ #include #include #include +#include #define UEVENT_HELPER_PATH_LEN 256 #define UEVENT_NUM_ENVP 32 /* number of env pointers */ @@ -65,6 +66,9 @@ struct kobject { struct kobj_type *ktype; struct sysfs_dirent *sd; struct kref kref; +#ifdef CONFIG_DEBUG_KOBJECT_RELEASE + struct delayed_work release; +#endif unsigned int state_initialized:1; unsigned int state_in_sysfs:1; unsigned int state_add_uevent_sent:1; diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 1501aa553221..444e1c12fea9 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -981,6 +981,25 @@ config DEBUG_KOBJECT If you say Y here, some extra kobject debugging messages will be sent to the syslog. +config DEBUG_KOBJECT_RELEASE + bool "kobject release debugging" + depends on DEBUG_KERNEL + help + kobjects are reference counted objects. This means that their + last reference count put is not predictable, and the kobject can + live on past the point at which a driver decides to drop it's + initial reference to the kobject gained on allocation. An + example of this would be a struct device which has just been + unregistered. + + However, some buggy drivers assume that after such an operation, + the memory backing the kobject can be immediately freed. This + goes completely against the principles of a refcounted object. + + If you say Y here, the kernel will delay the release of kobjects + on the last reference count to improve the visibility of this + kind of kobject release bug. + config HAVE_DEBUG_BUGVERBOSE bool diff --git a/lib/kobject.c b/lib/kobject.c index 4a1f33d43548..1d46c151a4ae 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -545,8 +545,8 @@ static void kobject_cleanup(struct kobject *kobj) struct kobj_type *t = get_ktype(kobj); const char *name = kobj->name; - pr_debug("kobject: '%s' (%p): %s\n", - kobject_name(kobj), kobj, __func__); + pr_debug("kobject: '%s' (%p): %s, parent %p\n", + kobject_name(kobj), kobj, __func__, kobj->parent); if (t && !t->release) pr_debug("kobject: '%s' (%p): does not have a release() " @@ -580,9 +580,25 @@ static void kobject_cleanup(struct kobject *kobj) } } +#ifdef CONFIG_DEBUG_KOBJECT_RELEASE +static void kobject_delayed_cleanup(struct work_struct *work) +{ + kobject_cleanup(container_of(to_delayed_work(work), + struct kobject, release)); +} +#endif + static void kobject_release(struct kref *kref) { - kobject_cleanup(container_of(kref, struct kobject, kref)); + struct kobject *kobj = container_of(kref, struct kobject, kref); +#ifdef CONFIG_DEBUG_KOBJECT_RELEASE + pr_debug("kobject: '%s' (%p): %s, parent %p (delayed)\n", + kobject_name(kobj), kobj, __func__, kobj->parent); + INIT_DELAYED_WORK(&kobj->release, kobject_delayed_cleanup); + schedule_delayed_work(&kobj->release, HZ); +#else + kobject_cleanup(kobj); +#endif } /** -- cgit v1.2.3-71-gd317 From cffe4e0e7413eb29fb8bd035c8b12b33a4b8522a Mon Sep 17 00:00:00 2001 From: Stratos Karafotis Date: Wed, 5 Jun 2013 19:01:50 +0300 Subject: cpufreq: Remove unused function __cpufreq_driver_getavg() The target frequency calculation method in the ondemand governor has changed and it is now independent of the measured average frequency. Consequently, the __cpufreq_driver_getavg() function and getavg member of struct cpufreq_driver are not used any more, so drop them. [rjw: Changelog] Signed-off-by: Stratos Karafotis Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 12 ------------ include/linux/cpufreq.h | 6 ------ 2 files changed, 18 deletions(-) (limited to 'include') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index a4ad7339588d..9a9d8ee9faec 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1593,18 +1593,6 @@ fail: } EXPORT_SYMBOL_GPL(cpufreq_driver_target); -int __cpufreq_driver_getavg(struct cpufreq_policy *policy, unsigned int cpu) -{ - if (cpufreq_disabled()) - return 0; - - if (!cpufreq_driver->getavg) - return 0; - - return cpufreq_driver->getavg(policy, cpu); -} -EXPORT_SYMBOL_GPL(__cpufreq_driver_getavg); - /* * when "event" is CPUFREQ_GOV_LIMITS */ diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 90d5a15120d5..e1fd215e16c9 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -216,10 +216,6 @@ extern int cpufreq_driver_target(struct cpufreq_policy *policy, extern int __cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation); - -extern int __cpufreq_driver_getavg(struct cpufreq_policy *policy, - unsigned int cpu); - int cpufreq_register_governor(struct cpufreq_governor *governor); void cpufreq_unregister_governor(struct cpufreq_governor *governor); @@ -258,8 +254,6 @@ struct cpufreq_driver { unsigned int (*get) (unsigned int cpu); /* optional */ - unsigned int (*getavg) (struct cpufreq_policy *policy, - unsigned int cpu); int (*bios_limit) (int cpu, unsigned int *limit); int (*exit) (struct cpufreq_policy *policy); -- cgit v1.2.3-71-gd317 From 33bdd5a88a0fb7fbd08947261b243fcec4ff089d Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 3 Jun 2013 04:23:48 -0300 Subject: [media] mem2mem: add support for hardware buffered queue On mem2mem decoders with a hardware bitstream ringbuffer, to drain the buffer at the end of the stream, remaining frames might need to be decoded from the bitstream buffer without additional input buffers being provided. To achieve this, allow a queue to be marked as buffered by the driver, and allow scheduling of device_runs when buffered ready queues are empty. This also allows a driver to copy input buffers into their bitstream ringbuffer and immediately mark them as done to be dequeued. The motivation for this patch is hardware assisted h.264 reordering support in the coda driver. For high profile streams, the coda can hold back out-of-order frames, causing a few mem2mem device runs in the beginning, that don't produce any decompressed buffer at the v4l2 capture side. At the same time, the last few frames can be decoded from the bitstream with mem2mem device runs that don't need a new input buffer at the v4l2 output side. The decoder command ioctl can be used to put the decoder into the ringbuffer draining end-of-stream mode. Signed-off-by: Philipp Zabel Acked-by: Sylwester Nawrocki Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 10 ++++++++-- include/media/v4l2-mem2mem.h | 13 +++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index e96497f7c3ed..89b90672088c 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -196,6 +196,10 @@ static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) * 2) at least one destination buffer has to be queued, * 3) streaming has to be on. * + * If a queue is buffered (for example a decoder hardware ringbuffer that has + * to be drained before doing streamoff), allow scheduling without v4l2 buffers + * on that queue. + * * There may also be additional, custom requirements. In such case the driver * should supply a custom callback (job_ready in v4l2_m2m_ops) that should * return 1 if the instance is ready. @@ -224,7 +228,8 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) } spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out); - if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)) { + if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue) + && !m2m_ctx->out_q_ctx.buffered) { spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags_out); spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); @@ -232,7 +237,8 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) return; } spin_lock_irqsave(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap); - if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) { + if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue) + && !m2m_ctx->cap_q_ctx.buffered) { spin_unlock_irqrestore(&m2m_ctx->cap_q_ctx.rdy_spinlock, flags_cap); spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 0f4555b2a31b..44542a20ab81 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -60,6 +60,7 @@ struct v4l2_m2m_queue_ctx { struct list_head rdy_queue; spinlock_t rdy_spinlock; u8 num_rdy; + bool buffered; }; struct v4l2_m2m_ctx { @@ -134,6 +135,18 @@ struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev, void *drv_priv, int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)); +static inline void v4l2_m2m_set_src_buffered(struct v4l2_m2m_ctx *m2m_ctx, + bool buffered) +{ + m2m_ctx->out_q_ctx.buffered = buffered; +} + +static inline void v4l2_m2m_set_dst_buffered(struct v4l2_m2m_ctx *m2m_ctx, + bool buffered) +{ + m2m_ctx->cap_q_ctx.buffered = buffered; +} + void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx); void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb); -- cgit v1.2.3-71-gd317 From 873229e4fdf34196aa5d707957c59ba54c25eaba Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Tue, 25 Jun 2013 11:17:34 -0300 Subject: [media] media: davinci: vpif: capture: add V4L2-async support Add support for asynchronous subdevice probing, using the v4l2-async API. The legacy synchronous mode is still supported too, which allows to gradually update drivers and platforms. Signed-off-by: Prabhakar Lad Cc: Guennadi Liakhovetski Cc: Hans Verkuil Cc: Laurent Pinchart Cc: Sakari Ailus Cc: Mauro Carvalho Chehab Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_capture.c | 151 ++++++++++++++++++-------- drivers/media/platform/davinci/vpif_capture.h | 2 + include/media/davinci/vpif_types.h | 2 + 3 files changed, 107 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 5514175bbd07..b11d7a74497e 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1979,6 +1979,76 @@ vpif_init_free_channel_objects: return err; } +static int vpif_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + int i; + + for (i = 0; i < vpif_obj.config->subdev_count; i++) + if (!strcmp(vpif_obj.config->subdev_info[i].name, + subdev->name)) { + vpif_obj.sd[i] = subdev; + return 0; + } + + return -EINVAL; +} + +static int vpif_probe_complete(void) +{ + struct common_obj *common; + struct channel_obj *ch; + int i, j, err, k; + + for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) { + ch = vpif_obj.dev[j]; + ch->channel_id = j; + common = &(ch->common[VPIF_VIDEO_INDEX]); + spin_lock_init(&common->irqlock); + mutex_init(&common->lock); + ch->video_dev->lock = &common->lock; + /* Initialize prio member of channel object */ + v4l2_prio_init(&ch->prio); + video_set_drvdata(ch->video_dev, ch); + + /* select input 0 */ + err = vpif_set_input(vpif_obj.config, ch, 0); + if (err) + goto probe_out; + + err = video_register_device(ch->video_dev, + VFL_TYPE_GRABBER, (j ? 1 : 0)); + if (err) + goto probe_out; + } + + v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n"); + return 0; + +probe_out: + for (k = 0; k < j; k++) { + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[k]; + /* Unregister video device */ + video_unregister_device(ch->video_dev); + } + kfree(vpif_obj.sd); + for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { + ch = vpif_obj.dev[i]; + /* Note: does nothing if ch->video_dev == NULL */ + video_device_release(ch->video_dev); + } + v4l2_device_unregister(&vpif_obj.v4l2_dev); + + return err; +} + +static int vpif_async_complete(struct v4l2_async_notifier *notifier) +{ + return vpif_probe_complete(); +} + /** * vpif_probe : This function probes the vpif capture driver * @pdev: platform device pointer @@ -1989,12 +2059,10 @@ vpif_init_free_channel_objects: static __init int vpif_probe(struct platform_device *pdev) { struct vpif_subdev_info *subdevdata; - struct vpif_capture_config *config; - int i, j, k, err; + int i, j, err; int res_idx = 0; struct i2c_adapter *i2c_adap; struct channel_obj *ch; - struct common_obj *common; struct video_device *vfd; struct resource *res; int subdev_count; @@ -2068,10 +2136,9 @@ static __init int vpif_probe(struct platform_device *pdev) } } - i2c_adap = i2c_get_adapter(1); - config = pdev->dev.platform_data; + vpif_obj.config = pdev->dev.platform_data; - subdev_count = config->subdev_count; + subdev_count = vpif_obj.config->subdev_count; vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count, GFP_KERNEL); if (vpif_obj.sd == NULL) { @@ -2080,54 +2147,42 @@ static __init int vpif_probe(struct platform_device *pdev) goto vpif_sd_error; } - for (i = 0; i < subdev_count; i++) { - subdevdata = &config->subdev_info[i]; - vpif_obj.sd[i] = - v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, - i2c_adap, - &subdevdata->board_info, - NULL); - - if (!vpif_obj.sd[i]) { - vpif_err("Error registering v4l2 subdevice\n"); - err = -ENODEV; + if (!vpif_obj.config->asd_sizes) { + i2c_adap = i2c_get_adapter(1); + for (i = 0; i < subdev_count; i++) { + subdevdata = &vpif_obj.config->subdev_info[i]; + vpif_obj.sd[i] = + v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, + i2c_adap, + &subdevdata-> + board_info, + NULL); + + if (!vpif_obj.sd[i]) { + vpif_err("Error registering v4l2 subdevice\n"); + goto probe_subdev_out; + } + v4l2_info(&vpif_obj.v4l2_dev, + "registered sub device %s\n", + subdevdata->name); + } + vpif_probe_complete(); + } else { + vpif_obj.notifier.subdev = vpif_obj.config->asd; + vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; + vpif_obj.notifier.bound = vpif_async_bound; + vpif_obj.notifier.complete = vpif_async_complete; + err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev, + &vpif_obj.notifier); + if (err) { + vpif_err("Error registering async notifier\n"); + err = -EINVAL; goto probe_subdev_out; } - v4l2_info(&vpif_obj.v4l2_dev, "registered sub device %s\n", - subdevdata->name); } - for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) { - ch = vpif_obj.dev[j]; - ch->channel_id = j; - common = &(ch->common[VPIF_VIDEO_INDEX]); - spin_lock_init(&common->irqlock); - mutex_init(&common->lock); - ch->video_dev->lock = &common->lock; - /* Initialize prio member of channel object */ - v4l2_prio_init(&ch->prio); - video_set_drvdata(ch->video_dev, ch); - - /* select input 0 */ - err = vpif_set_input(config, ch, 0); - if (err) - goto probe_out; - - err = video_register_device(ch->video_dev, - VFL_TYPE_GRABBER, (j ? 1 : 0)); - if (err) - goto probe_out; - } - v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n"); return 0; -probe_out: - for (k = 0; k < j; k++) { - /* Get the pointer to the channel object */ - ch = vpif_obj.dev[k]; - /* Unregister video device */ - video_unregister_device(ch->video_dev); - } probe_subdev_out: /* free sub devices memory */ kfree(vpif_obj.sd); diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h index 0ebb31260369..5a29d9a0cae1 100644 --- a/drivers/media/platform/davinci/vpif_capture.h +++ b/drivers/media/platform/davinci/vpif_capture.h @@ -142,6 +142,8 @@ struct vpif_device { struct v4l2_device v4l2_dev; struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS]; struct v4l2_subdev **sd; + struct v4l2_async_notifier notifier; + struct vpif_capture_config *config; }; struct vpif_config_params { diff --git a/include/media/davinci/vpif_types.h b/include/media/davinci/vpif_types.h index 3882e0675ccf..e08bcde52d05 100644 --- a/include/media/davinci/vpif_types.h +++ b/include/media/davinci/vpif_types.h @@ -81,5 +81,7 @@ struct vpif_capture_config { struct vpif_subdev_info *subdev_info; int subdev_count; const char *card_name; + struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ + int *asd_sizes; /* 0-terminated array of asd group sizes */ }; #endif /* _VPIF_TYPES_H */ -- cgit v1.2.3-71-gd317 From 4b8a531e6bb0686203e9cf82a54dfe189de7d5c2 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Tue, 25 Jun 2013 11:17:35 -0300 Subject: [media] media: davinci: vpif: display: add V4L2-async support Add support for asynchronous subdevice probing, using the v4l2-async API. The legacy synchronous mode is still supported too, which allows to gradually update drivers and platforms. Signed-off-by: Lad, Prabhakar Cc: Guennadi Liakhovetski Cc: Hans Verkuil Cc: Laurent Pinchart Cc: Sakari Ailus Cc: Mauro Carvalho Chehab Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_display.c | 210 ++++++++++++++++---------- drivers/media/platform/davinci/vpif_display.h | 3 +- include/media/davinci/vpif_types.h | 2 + 3 files changed, 132 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index e6e573650250..c2ff06745fdc 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1618,6 +1618,102 @@ vpif_init_free_channel_objects: return err; } +static int vpif_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + int i; + + for (i = 0; i < vpif_obj.config->subdev_count; i++) + if (!strcmp(vpif_obj.config->subdevinfo[i].name, + subdev->name)) { + vpif_obj.sd[i] = subdev; + vpif_obj.sd[i]->grp_id = 1 << i; + return 0; + } + + return -EINVAL; +} + +static int vpif_probe_complete(void) +{ + struct common_obj *common; + struct channel_obj *ch; + int j, err, k; + + for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) { + ch = vpif_obj.dev[j]; + /* Initialize field of the channel objects */ + atomic_set(&ch->usrs, 0); + for (k = 0; k < VPIF_NUMOBJECTS; k++) { + ch->common[k].numbuffers = 0; + common = &ch->common[k]; + common->io_usrs = 0; + common->started = 0; + spin_lock_init(&common->irqlock); + mutex_init(&common->lock); + common->numbuffers = 0; + common->set_addr = NULL; + common->ytop_off = 0; + common->ybtm_off = 0; + common->ctop_off = 0; + common->cbtm_off = 0; + common->cur_frm = NULL; + common->next_frm = NULL; + memset(&common->fmt, 0, sizeof(common->fmt)); + common->numbuffers = config_params.numbuffers[k]; + } + ch->initialized = 0; + if (vpif_obj.config->subdev_count) + ch->sd = vpif_obj.sd[0]; + ch->channel_id = j; + if (j < 2) + ch->common[VPIF_VIDEO_INDEX].numbuffers = + config_params.numbuffers[ch->channel_id]; + else + ch->common[VPIF_VIDEO_INDEX].numbuffers = 0; + + memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); + + /* Initialize prio member of channel object */ + v4l2_prio_init(&ch->prio); + ch->common[VPIF_VIDEO_INDEX].fmt.type = + V4L2_BUF_TYPE_VIDEO_OUTPUT; + ch->video_dev->lock = &common->lock; + video_set_drvdata(ch->video_dev, ch); + + /* select output 0 */ + err = vpif_set_output(vpif_obj.config, ch, 0); + if (err) + goto probe_out; + + /* register video device */ + vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n", + (int)ch, (int)&ch->video_dev); + + err = video_register_device(ch->video_dev, + VFL_TYPE_GRABBER, (j ? 3 : 2)); + if (err < 0) + goto probe_out; + } + + return 0; + +probe_out: + for (k = 0; k < j; k++) { + ch = vpif_obj.dev[k]; + video_unregister_device(ch->video_dev); + video_device_release(ch->video_dev); + ch->video_dev = NULL; + } + return err; +} + +static int vpif_async_complete(struct v4l2_async_notifier *notifier) +{ + return vpif_probe_complete(); +} + /* * vpif_probe: This function creates device entries by register itself to the * V4L2 driver and initializes fields of each channel objects @@ -1625,11 +1721,9 @@ vpif_init_free_channel_objects: static __init int vpif_probe(struct platform_device *pdev) { struct vpif_subdev_info *subdevdata; - struct vpif_display_config *config; - int i, j = 0, k, err = 0; + int i, j = 0, err = 0; int res_idx = 0; struct i2c_adapter *i2c_adap; - struct common_obj *common; struct channel_obj *ch; struct video_device *vfd; struct resource *res; @@ -1708,11 +1802,9 @@ static __init int vpif_probe(struct platform_device *pdev) size/2; } } - - i2c_adap = i2c_get_adapter(1); - config = pdev->dev.platform_data; - subdev_count = config->subdev_count; - subdevdata = config->subdevinfo; + vpif_obj.config = pdev->dev.platform_data; + subdev_count = vpif_obj.config->subdev_count; + subdevdata = vpif_obj.config->subdevinfo; vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count, GFP_KERNEL); if (vpif_obj.sd == NULL) { @@ -1721,86 +1813,40 @@ static __init int vpif_probe(struct platform_device *pdev) goto vpif_sd_error; } - for (i = 0; i < subdev_count; i++) { - vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, - i2c_adap, - &subdevdata[i].board_info, - NULL); - if (!vpif_obj.sd[i]) { - vpif_err("Error registering v4l2 subdevice\n"); - err = -ENODEV; - goto probe_subdev_out; - } - - if (vpif_obj.sd[i]) - vpif_obj.sd[i]->grp_id = 1 << i; - } - - for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) { - ch = vpif_obj.dev[j]; - /* Initialize field of the channel objects */ - atomic_set(&ch->usrs, 0); - for (k = 0; k < VPIF_NUMOBJECTS; k++) { - ch->common[k].numbuffers = 0; - common = &ch->common[k]; - common->io_usrs = 0; - common->started = 0; - spin_lock_init(&common->irqlock); - mutex_init(&common->lock); - common->numbuffers = 0; - common->set_addr = NULL; - common->ytop_off = common->ybtm_off = 0; - common->ctop_off = common->cbtm_off = 0; - common->cur_frm = common->next_frm = NULL; - memset(&common->fmt, 0, sizeof(common->fmt)); - common->numbuffers = config_params.numbuffers[k]; + if (!vpif_obj.config->asd_sizes) { + i2c_adap = i2c_get_adapter(1); + for (i = 0; i < subdev_count; i++) { + vpif_obj.sd[i] = + v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, + i2c_adap, + &subdevdata[i]. + board_info, + NULL); + if (!vpif_obj.sd[i]) { + vpif_err("Error registering v4l2 subdevice\n"); + goto probe_subdev_out; + } + if (vpif_obj.sd[i]) + vpif_obj.sd[i]->grp_id = 1 << i; + } + vpif_probe_complete(); + } else { + vpif_obj.notifier.subdev = vpif_obj.config->asd; + vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; + vpif_obj.notifier.bound = vpif_async_bound; + vpif_obj.notifier.complete = vpif_async_complete; + err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev, + &vpif_obj.notifier); + if (err) { + vpif_err("Error registering async notifier\n"); + err = -EINVAL; + goto probe_subdev_out; } - ch->initialized = 0; - if (subdev_count) - ch->sd = vpif_obj.sd[0]; - ch->channel_id = j; - if (j < 2) - ch->common[VPIF_VIDEO_INDEX].numbuffers = - config_params.numbuffers[ch->channel_id]; - else - ch->common[VPIF_VIDEO_INDEX].numbuffers = 0; - - memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); - - /* Initialize prio member of channel object */ - v4l2_prio_init(&ch->prio); - ch->common[VPIF_VIDEO_INDEX].fmt.type = - V4L2_BUF_TYPE_VIDEO_OUTPUT; - ch->video_dev->lock = &common->lock; - video_set_drvdata(ch->video_dev, ch); - - /* select output 0 */ - err = vpif_set_output(config, ch, 0); - if (err) - goto probe_out; - - /* register video device */ - vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n", - (int)ch, (int)&ch->video_dev); - - err = video_register_device(ch->video_dev, - VFL_TYPE_GRABBER, (j ? 3 : 2)); - if (err < 0) - goto probe_out; } - v4l2_info(&vpif_obj.v4l2_dev, - " VPIF display driver initialized\n"); return 0; -probe_out: - for (k = 0; k < j; k++) { - ch = vpif_obj.dev[k]; - video_unregister_device(ch->video_dev); - video_device_release(ch->video_dev); - ch->video_dev = NULL; - } probe_subdev_out: kfree(vpif_obj.sd); vpif_sd_error: diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h index 5d87fc86e580..4d0485b99a80 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/davinci/vpif_display.h @@ -148,7 +148,8 @@ struct vpif_device { struct v4l2_device v4l2_dev; struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS]; struct v4l2_subdev **sd; - + struct v4l2_async_notifier notifier; + struct vpif_display_config *config; }; struct vpif_config_params { diff --git a/include/media/davinci/vpif_types.h b/include/media/davinci/vpif_types.h index e08bcde52d05..3cb1704a0650 100644 --- a/include/media/davinci/vpif_types.h +++ b/include/media/davinci/vpif_types.h @@ -59,6 +59,8 @@ struct vpif_display_config { int subdev_count; struct vpif_display_chan_config chan_config[VPIF_DISPLAY_MAX_CHANNELS]; const char *card_name; + struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ + int *asd_sizes; /* 0-terminated array of asd group sizes */ }; struct vpif_input { -- cgit v1.2.3-71-gd317 From 5e95814ff3f2a6ea7d76e822bbc3b0c0b94495a4 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sat, 20 Jul 2013 02:21:05 -0300 Subject: [media] media: i2c: adv7343: make the platform data members as array This patch makes the platform data members as array wherever possible, so as this makes easier while collecting the data in DT case and read the entire array at once. This patch also makes appropriate changes to board-da850-evm.c Signed-off-by: Lad, Prabhakar Acked-by: Sekhar Nori Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-davinci/board-da850-evm.c | 6 ++---- drivers/media/i2c/adv7343.c | 28 ++++++++++++++-------------- include/media/adv7343.h | 20 ++++---------------- 3 files changed, 20 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index bea6793a7ede..9f09f45835f8 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1249,12 +1249,10 @@ static struct vpif_capture_config da850_vpif_capture_config = { static struct adv7343_platform_data adv7343_pdata = { .mode_config = { - .dac_3 = 1, - .dac_2 = 1, - .dac_1 = 1, + .dac = { 1, 1, 1 }, }, .sd_config = { - .sd_dac_out1 = 1, + .sd_dac_out = { 1 }, }, }; diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c index 8080c2cf1029..f0238fb3e3e2 100644 --- a/drivers/media/i2c/adv7343.c +++ b/drivers/media/i2c/adv7343.c @@ -227,12 +227,12 @@ static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) else val = state->pdata->mode_config.sleep_mode << 0 | state->pdata->mode_config.pll_control << 1 | - state->pdata->mode_config.dac_3 << 2 | - state->pdata->mode_config.dac_2 << 3 | - state->pdata->mode_config.dac_1 << 4 | - state->pdata->mode_config.dac_6 << 5 | - state->pdata->mode_config.dac_5 << 6 | - state->pdata->mode_config.dac_4 << 7; + state->pdata->mode_config.dac[2] << 2 | + state->pdata->mode_config.dac[1] << 3 | + state->pdata->mode_config.dac[0] << 4 | + state->pdata->mode_config.dac[5] << 5 | + state->pdata->mode_config.dac[4] << 6 | + state->pdata->mode_config.dac[3] << 7; err = adv7343_write(sd, ADV7343_POWER_MODE_REG, val); if (err < 0) @@ -251,15 +251,15 @@ static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) /* configure SD DAC Output 2 and SD DAC Output 1 bit to zero */ val = state->reg82 & (SD_DAC_1_DI & SD_DAC_2_DI); - if (state->pdata && state->pdata->sd_config.sd_dac_out1) - val = val | (state->pdata->sd_config.sd_dac_out1 << 1); - else if (state->pdata && !state->pdata->sd_config.sd_dac_out1) - val = val & ~(state->pdata->sd_config.sd_dac_out1 << 1); + if (state->pdata && state->pdata->sd_config.sd_dac_out[0]) + val = val | (state->pdata->sd_config.sd_dac_out[0] << 1); + else if (state->pdata && !state->pdata->sd_config.sd_dac_out[0]) + val = val & ~(state->pdata->sd_config.sd_dac_out[0] << 1); - if (state->pdata && state->pdata->sd_config.sd_dac_out2) - val = val | (state->pdata->sd_config.sd_dac_out2 << 2); - else if (state->pdata && !state->pdata->sd_config.sd_dac_out2) - val = val & ~(state->pdata->sd_config.sd_dac_out2 << 2); + if (state->pdata && state->pdata->sd_config.sd_dac_out[1]) + val = val | (state->pdata->sd_config.sd_dac_out[1] << 2); + else if (state->pdata && !state->pdata->sd_config.sd_dac_out[1]) + val = val & ~(state->pdata->sd_config.sd_dac_out[1] << 2); err = adv7343_write(sd, ADV7343_SD_MODE_REG2, val); if (err < 0) diff --git a/include/media/adv7343.h b/include/media/adv7343.h index 944757be49bb..e4142b1ef8cd 100644 --- a/include/media/adv7343.h +++ b/include/media/adv7343.h @@ -28,12 +28,7 @@ * @pll_control: PLL and oversampling control. This control allows internal * PLL 1 circuit to be powered down and the oversampling to be * switched off. - * @dac_1: power on/off DAC 1. - * @dac_2: power on/off DAC 2. - * @dac_3: power on/off DAC 3. - * @dac_4: power on/off DAC 4. - * @dac_5: power on/off DAC 5. - * @dac_6: power on/off DAC 6. + * @dac: array to configure power on/off DAC's 1..6 * * Power mode register (Register 0x0), for more info refer REGISTER MAP ACCESS * section of datasheet[1], table 17 page no 30. @@ -43,23 +38,16 @@ struct adv7343_power_mode { bool sleep_mode; bool pll_control; - bool dac_1; - bool dac_2; - bool dac_3; - bool dac_4; - bool dac_5; - bool dac_6; + u32 dac[6]; }; /** * struct adv7343_sd_config - SD Only Output Configuration. - * @sd_dac_out1: Configure SD DAC Output 1. - * @sd_dac_out2: Configure SD DAC Output 2. + * @sd_dac_out: array configuring SD DAC Outputs 1 and 2 */ struct adv7343_sd_config { /* SD only Output Configuration */ - bool sd_dac_out1; - bool sd_dac_out2; + u32 sd_dac_out[2]; }; /** -- cgit v1.2.3-71-gd317 From 8fd79579c9ccc6ac98d02c2c97d5fb367cfc0e29 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Tue, 14 May 2013 16:54:43 -0300 Subject: [media] tea575x-tuner: move HW init to a separate function Move HW initialization to separate function to allow using the code without the v4l parts. This is needed for use in the bttv driver. Signed-off-by: Ondrej Zary Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/sound/tea575x-tuner.h | 1 + sound/i2c/other/tea575x-tuner.c | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h index 098c4de44945..2d4fa59db902 100644 --- a/include/sound/tea575x-tuner.h +++ b/include/sound/tea575x-tuner.h @@ -71,6 +71,7 @@ struct snd_tea575x { int (*ext_init)(struct snd_tea575x *tea); }; +int snd_tea575x_hw_init(struct snd_tea575x *tea); int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner); void snd_tea575x_exit(struct snd_tea575x *tea); void snd_tea575x_set_freq(struct snd_tea575x *tea); diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index 8a36a1d9803f..46ec4dff094b 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -486,13 +486,9 @@ static const struct v4l2_ctrl_ops tea575x_ctrl_ops = { .s_ctrl = tea575x_s_ctrl, }; -/* - * initialize all the tea575x chips - */ -int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner) -{ - int retval; +int snd_tea575x_hw_init(struct snd_tea575x *tea) +{ tea->mute = true; /* Not all devices can or know how to read the data back. @@ -507,6 +503,17 @@ int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner) tea->freq = 90500 * 16; /* 90.5Mhz default */ snd_tea575x_set_freq(tea); + return 0; +} +EXPORT_SYMBOL(snd_tea575x_hw_init); + +int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner) +{ + int retval = snd_tea575x_hw_init(tea); + + if (retval) + return retval; + tea->vd = tea575x_radio; video_set_drvdata(&tea->vd, tea); mutex_init(&tea->mutex); -- cgit v1.2.3-71-gd317 From 102c9323c35a83789ad5ebd3c45fa8fb389add88 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 12 Jul 2013 17:07:27 -0400 Subject: tracing: Add __tracepoint_string() to export string pointers There are several tracepoints (mostly in RCU), that reference a string pointer and uses the print format of "%s" to display the string that exists in the kernel, instead of copying the actual string to the ring buffer (saves time and ring buffer space). But this has an issue with userspace tools that read the binary buffers that has the address of the string but has no access to what the string itself is. The end result is just output that looks like: rcu_dyntick: ffffffff818adeaa 1 0 rcu_dyntick: ffffffff818adeb5 0 140000000000000 rcu_dyntick: ffffffff818adeb5 0 140000000000000 rcu_utilization: ffffffff8184333b rcu_utilization: ffffffff8184333b The above is pretty useless when read by the userspace tools. Ideally we would want something that looks like this: rcu_dyntick: Start 1 0 rcu_dyntick: End 0 140000000000000 rcu_dyntick: Start 140000000000000 0 rcu_callback: rcu_preempt rhp=0xffff880037aff710 func=put_cred_rcu 0/4 rcu_callback: rcu_preempt rhp=0xffff880078961980 func=file_free_rcu 0/5 rcu_dyntick: End 0 1 The trace_printk() which also only stores the address of the string format instead of recording the string into the buffer itself, exports the mapping of kernel addresses to format strings via the printk_format file in the debugfs tracing directory. The tracepoint strings can use this same method and output the format to the same file and the userspace tools will be able to decipher the address without any modification. The tracepoint strings need its own section to save the strings because the trace_printk section will cause the trace_printk() buffers to be allocated if anything exists within the section. trace_printk() is only used for debugging and should never exist in the kernel, we can not use the trace_printk sections. Add a new tracepoint_str section that will also be examined by the output of the printk_format file. Cc: Paul E. McKenney Signed-off-by: Steven Rostedt --- include/asm-generic/vmlinux.lds.h | 7 ++++++- include/linux/ftrace_event.h | 34 ++++++++++++++++++++++++++++++++++ kernel/trace/trace.h | 3 +++ kernel/trace/trace_printk.c | 19 +++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 69732d279e8b..83e2c31e8b00 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -122,8 +122,12 @@ #define TRACE_PRINTKS() VMLINUX_SYMBOL(__start___trace_bprintk_fmt) = .; \ *(__trace_printk_fmt) /* Trace_printk fmt' pointer */ \ VMLINUX_SYMBOL(__stop___trace_bprintk_fmt) = .; +#define TRACEPOINT_STR() VMLINUX_SYMBOL(__start___tracepoint_str) = .; \ + *(__tracepoint_str) /* Trace_printk fmt' pointer */ \ + VMLINUX_SYMBOL(__stop___tracepoint_str) = .; #else #define TRACE_PRINTKS() +#define TRACEPOINT_STR() #endif #ifdef CONFIG_FTRACE_SYSCALLS @@ -190,7 +194,8 @@ VMLINUX_SYMBOL(__stop___verbose) = .; \ LIKELY_PROFILE() \ BRANCH_PROFILE() \ - TRACE_PRINTKS() + TRACE_PRINTKS() \ + TRACEPOINT_STR() /* * Data section helpers diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 4372658c73ae..81af18a75f4d 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -357,6 +357,40 @@ do { \ __trace_printk(ip, fmt, ##args); \ } while (0) +/** + * tracepoint_string - register constant persistent string to trace system + * @str - a constant persistent string that will be referenced in tracepoints + * + * If constant strings are being used in tracepoints, it is faster and + * more efficient to just save the pointer to the string and reference + * that with a printf "%s" instead of saving the string in the ring buffer + * and wasting space and time. + * + * The problem with the above approach is that userspace tools that read + * the binary output of the trace buffers do not have access to the string. + * Instead they just show the address of the string which is not very + * useful to users. + * + * With tracepoint_string(), the string will be registered to the tracing + * system and exported to userspace via the debugfs/tracing/printk_formats + * file that maps the string address to the string text. This way userspace + * tools that read the binary buffers have a way to map the pointers to + * the ASCII strings they represent. + * + * The @str used must be a constant string and persistent as it would not + * make sense to show a string that no longer exists. But it is still fine + * to be used with modules, because when modules are unloaded, if they + * had tracepoints, the ring buffers are cleared too. As long as the string + * does not change during the life of the module, it is fine to use + * tracepoint_string() within a module. + */ +#define tracepoint_string(str) \ + ({ \ + static const char *___tp_str __tracepoint_string = str; \ + ___tp_str; \ + }) +#define __tracepoint_string __attribute__((section("__tracepoint_str"))) + #ifdef CONFIG_PERF_EVENTS struct perf_event; diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 4a4f6e1828b6..ba321f12df8c 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -1022,6 +1022,9 @@ extern struct list_head ftrace_events; extern const char *__start___trace_bprintk_fmt[]; extern const char *__stop___trace_bprintk_fmt[]; +extern const char *__start___tracepoint_str[]; +extern const char *__stop___tracepoint_str[]; + void trace_printk_init_buffers(void); void trace_printk_start_comm(void); int trace_keep_overwrite(struct tracer *tracer, u32 mask, int set); diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c index a9077c1b4ad3..2900817ba65c 100644 --- a/kernel/trace/trace_printk.c +++ b/kernel/trace/trace_printk.c @@ -244,12 +244,31 @@ static const char **find_next(void *v, loff_t *pos) { const char **fmt = v; int start_index; + int last_index; start_index = __stop___trace_bprintk_fmt - __start___trace_bprintk_fmt; if (*pos < start_index) return __start___trace_bprintk_fmt + *pos; + /* + * The __tracepoint_str section is treated the same as the + * __trace_printk_fmt section. The difference is that the + * __trace_printk_fmt section should only be used by trace_printk() + * in a debugging environment, as if anything exists in that section + * the trace_prink() helper buffers are allocated, which would just + * waste space in a production environment. + * + * The __tracepoint_str sections on the other hand are used by + * tracepoints which need to map pointers to their strings to + * the ASCII text for userspace. + */ + last_index = start_index; + start_index = __stop___tracepoint_str - __start___tracepoint_str; + + if (*pos < last_index + start_index) + return __start___tracepoint_str + (*pos - last_index); + return find_next_mod_format(start_index, v, fmt, pos); } -- cgit v1.2.3-71-gd317 From 10d8b34a421716d55a4ff7c2d427c35e88b8fd60 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 29 Jun 2013 10:44:17 +0400 Subject: serial: max310x: Driver rework This patch rework max310x driver. Major changes have been made: - Prepare driver to support ICs with more than one UART. - Prepare driver to support work with I2C-bus. The patch changes almost every function and can not be divided into parts. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 4 +- drivers/tty/serial/max310x.c | 918 +++++++++++++++++----------------- include/linux/platform_data/max310x.h | 5 +- 3 files changed, 454 insertions(+), 473 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 5e3d68917ffe..25772c15276d 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -291,9 +291,9 @@ config SERIAL_MAX3100 config SERIAL_MAX310X bool "MAX310X support" - depends on SPI + depends on SPI_MASTER select SERIAL_CORE - select REGMAP_SPI if SPI + select REGMAP_SPI if SPI_MASTER default n help This selects support for an advanced UART from Maxim (Dallas). diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 8941e6418942..4620289e9e49 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1,7 +1,7 @@ /* * Maxim (Dallas) MAX3107/8 serial driver * - * Copyright (C) 2012 Alexander Shiyan + * Copyright (C) 2012-2013 Alexander Shiyan * * Based on max3100.c, by Christian Pellegrin * Based on max3110.c, by Feng Tang @@ -17,7 +17,9 @@ /* TODO: MAX14830 support (Quad) */ #include +#include #include +#include #include #include #include @@ -25,8 +27,10 @@ #include #include #include + #include +#define MAX310X_NAME "max310x" #define MAX310X_MAJOR 204 #define MAX310X_MINOR 209 @@ -37,7 +41,8 @@ #define MAX310X_IRQSTS_REG (0x02) /* IRQ status */ #define MAX310X_LSR_IRQEN_REG (0x03) /* LSR IRQ enable */ #define MAX310X_LSR_IRQSTS_REG (0x04) /* LSR IRQ status */ -#define MAX310X_SPCHR_IRQEN_REG (0x05) /* Special char IRQ enable */ +#define MAX310X_REG_05 (0x05) +#define MAX310X_SPCHR_IRQEN_REG MAX310X_REG_05 /* Special char IRQ en */ #define MAX310X_SPCHR_IRQSTS_REG (0x06) /* Special char IRQ status */ #define MAX310X_STS_IRQEN_REG (0x07) /* Status IRQ enable */ #define MAX310X_STS_IRQSTS_REG (0x08) /* Status IRQ status */ @@ -63,8 +68,15 @@ #define MAX310X_BRGDIVLSB_REG (0x1c) /* Baud rate divisor LSB */ #define MAX310X_BRGDIVMSB_REG (0x1d) /* Baud rate divisor MSB */ #define MAX310X_CLKSRC_REG (0x1e) /* Clock source */ -/* Only present in MAX3107 */ -#define MAX3107_REVID_REG (0x1f) /* Revision identification */ +#define MAX310X_REG_1F (0x1f) + +#define MAX310X_REVID_REG MAX310X_REG_1F /* Revision ID */ + +#define MAX310X_GLOBALIRQ_REG MAX310X_REG_1F /* Global IRQ (RO) */ +#define MAX310X_GLOBALCMD_REG MAX310X_REG_1F /* Global Command (WO) */ + +/* Extended registers */ +#define MAX310X_REVID_EXTREG MAX310X_REG_05 /* Revision ID */ /* IRQ register bits */ #define MAX310X_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ @@ -246,58 +258,139 @@ #define MAX310X_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */ #define MAX310X_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */ +/* Global commands */ +#define MAX310X_EXTREG_ENBL (0xce) +#define MAX310X_EXTREG_DSBL (0xcd) + /* Misc definitions */ #define MAX310X_FIFO_SIZE (128) +#define MAX310x_REV_MASK (0xfc) /* MAX3107 specific */ #define MAX3107_REV_ID (0xa0) -#define MAX3107_REV_MASK (0xfe) - -/* IRQ status bits definitions */ -#define MAX310X_IRQ_TX (MAX310X_IRQ_TXFIFO_BIT | \ - MAX310X_IRQ_TXEMPTY_BIT) -#define MAX310X_IRQ_RX (MAX310X_IRQ_RXFIFO_BIT | \ - MAX310X_IRQ_RXEMPTY_BIT) - -/* Supported chip types */ -enum { - MAX310X_TYPE_MAX3107 = 3107, - MAX310X_TYPE_MAX3108 = 3108, + +struct max310x_devtype { + char name[9]; + int nr; + int (*detect)(struct device *); + void (*power)(struct uart_port *, int); }; -struct max310x_port { - struct uart_driver uart; +struct max310x_one { struct uart_port port; + struct work_struct tx_work; +}; - const char *name; - int uartclk; - - unsigned int nr_gpio; +struct max310x_port { + struct uart_driver uart; + struct max310x_devtype *devtype; + struct regmap *regmap; + struct regmap_config regcfg; + struct mutex mutex; + struct max310x_pdata *pdata; + int gpio_used; #ifdef CONFIG_GPIOLIB struct gpio_chip gpio; #endif + struct max310x_one p[0]; +}; - struct regmap *regmap; - struct regmap_config regcfg; +static u8 max310x_port_read(struct uart_port *port, u8 reg) +{ + struct max310x_port *s = dev_get_drvdata(port->dev); + unsigned int val = 0; - struct workqueue_struct *wq; - struct work_struct tx_work; + regmap_read(s->regmap, port->iobase + reg, &val); - struct mutex max310x_mutex; + return val; +} - struct max310x_pdata *pdata; +static void max310x_port_write(struct uart_port *port, u8 reg, u8 val) +{ + struct max310x_port *s = dev_get_drvdata(port->dev); + + regmap_write(s->regmap, port->iobase + reg, val); +} + +static void max310x_port_update(struct uart_port *port, u8 reg, u8 mask, u8 val) +{ + struct max310x_port *s = dev_get_drvdata(port->dev); + + regmap_update_bits(s->regmap, port->iobase + reg, mask, val); +} + +static int max3107_detect(struct device *dev) +{ + struct max310x_port *s = dev_get_drvdata(dev); + unsigned int val = 0; + int ret; + + ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val); + if (ret) + return ret; + + if (((val & MAX310x_REV_MASK) != MAX3107_REV_ID)) { + dev_err(dev, + "%s ID 0x%02x does not match\n", s->devtype->name, val); + return -ENODEV; + } + + return 0; +} + +static int max3108_detect(struct device *dev) +{ + struct max310x_port *s = dev_get_drvdata(dev); + unsigned int val = 0; + int ret; + + /* MAX3108 have not REV ID register, we just check default value + * from clocksource register to make sure everything works. + */ + ret = regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val); + if (ret) + return ret; + + if (val != (MAX310X_CLKSRC_EXTCLK_BIT | MAX310X_CLKSRC_PLLBYP_BIT)) { + dev_err(dev, "%s not present\n", s->devtype->name); + return -ENODEV; + } + + return 0; +} + +static void max310x_power(struct uart_port *port, int on) +{ + max310x_port_update(port, MAX310X_MODE1_REG, + MAX310X_MODE1_FORCESLEEP_BIT, + on ? 0 : MAX310X_MODE1_FORCESLEEP_BIT); + if (on) + msleep(50); +} + +static const struct max310x_devtype max3107_devtype = { + .name = "MAX3107", + .nr = 1, + .detect = max3107_detect, + .power = max310x_power, }; -static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg) +static const struct max310x_devtype max3108_devtype = { + .name = "MAX3108", + .nr = 1, + .detect = max3108_detect, + .power = max310x_power, +}; + +static bool max310x_reg_writeable(struct device *dev, unsigned int reg) { - switch (reg) { + switch (reg & 0x1f) { case MAX310X_IRQSTS_REG: case MAX310X_LSR_IRQSTS_REG: case MAX310X_SPCHR_IRQSTS_REG: case MAX310X_STS_IRQSTS_REG: case MAX310X_TXFIFOLVL_REG: case MAX310X_RXFIFOLVL_REG: - case MAX3107_REVID_REG: /* Only available on MAX3107 */ return false; default: break; @@ -308,7 +401,7 @@ static bool max3107_8_reg_writeable(struct device *dev, unsigned int reg) static bool max310x_reg_volatile(struct device *dev, unsigned int reg) { - switch (reg) { + switch (reg & 0x1f) { case MAX310X_RHR_REG: case MAX310X_IRQSTS_REG: case MAX310X_LSR_IRQSTS_REG: @@ -317,6 +410,9 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg) case MAX310X_TXFIFOLVL_REG: case MAX310X_RXFIFOLVL_REG: case MAX310X_GPIODATA_REG: + case MAX310X_BRGDIVLSB_REG: + case MAX310X_REG_05: + case MAX310X_REG_1F: return true; default: break; @@ -327,7 +423,7 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg) static bool max310x_reg_precious(struct device *dev, unsigned int reg) { - switch (reg) { + switch (reg & 0x1f) { case MAX310X_RHR_REG: case MAX310X_IRQSTS_REG: case MAX310X_SPCHR_IRQSTS_REG: @@ -340,42 +436,25 @@ static bool max310x_reg_precious(struct device *dev, unsigned int reg) return false; } -static void max310x_set_baud(struct max310x_port *s, int baud) +static void max310x_set_baud(struct uart_port *port, int baud) { - unsigned int mode = 0, div = s->uartclk / baud; + unsigned int mode = 0, div = port->uartclk / baud; if (!(div / 16)) { /* Mode x2 */ mode = MAX310X_BRGCFG_2XMODE_BIT; - div = (s->uartclk * 2) / baud; + div = (port->uartclk * 2) / baud; } if (!(div / 16)) { /* Mode x4 */ mode = MAX310X_BRGCFG_4XMODE_BIT; - div = (s->uartclk * 4) / baud; + div = (port->uartclk * 4) / baud; } - regmap_write(s->regmap, MAX310X_BRGDIVMSB_REG, - ((div / 16) >> 8) & 0xff); - regmap_write(s->regmap, MAX310X_BRGDIVLSB_REG, (div / 16) & 0xff); - regmap_write(s->regmap, MAX310X_BRGCFG_REG, (div % 16) | mode); -} - -static void max310x_wait_pll(struct max310x_port *s) -{ - int tryes = 1000; - - /* Wait for PLL only if crystal is used */ - if (!(s->pdata->driver_flags & MAX310X_EXT_CLK)) { - unsigned int sts = 0; - - while (tryes--) { - regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &sts); - if (sts & MAX310X_STS_CLKREADY_BIT) - break; - } - } + max310x_port_write(port, MAX310X_BRGDIVMSB_REG, (div / 16) >> 8); + max310x_port_write(port, MAX310X_BRGDIVLSB_REG, div / 16); + max310x_port_write(port, MAX310X_BRGCFG_REG, (div % 16) | mode); } static int max310x_update_best_err(unsigned long f, long *besterr) @@ -449,49 +528,49 @@ static int max310x_set_ref_clk(struct max310x_port *s) regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc); - if (pllcfg) - max310x_wait_pll(s); - - dev_dbg(s->port.dev, "Reference clock set to %lu Hz\n", bestfreq); + /* Wait for crystal */ + if (pllcfg && !(s->pdata->driver_flags & MAX310X_EXT_CLK)) + msleep(10); return (int)bestfreq; } -static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen) +static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) { - unsigned int sts = 0, ch = 0, flag; + unsigned int sts, ch, flag; - if (unlikely(rxlen >= MAX310X_FIFO_SIZE)) { - dev_warn(s->port.dev, "Possible RX FIFO overrun %d\n", rxlen); + if (unlikely(rxlen >= port->fifosize)) { + dev_warn_ratelimited(port->dev, + "Port %i: Possible RX FIFO overrun\n", + port->line); + port->icount.buf_overrun++; /* Ensure sanity of RX level */ - rxlen = MAX310X_FIFO_SIZE; + rxlen = port->fifosize; } - dev_dbg(s->port.dev, "RX Len = %u\n", rxlen); - while (rxlen--) { - regmap_read(s->regmap, MAX310X_RHR_REG, &ch); - regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &sts); + ch = max310x_port_read(port, MAX310X_RHR_REG); + sts = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG); sts &= MAX310X_LSR_RXPAR_BIT | MAX310X_LSR_FRERR_BIT | MAX310X_LSR_RXOVR_BIT | MAX310X_LSR_RXBRK_BIT; - s->port.icount.rx++; + port->icount.rx++; flag = TTY_NORMAL; if (unlikely(sts)) { if (sts & MAX310X_LSR_RXBRK_BIT) { - s->port.icount.brk++; - if (uart_handle_break(&s->port)) + port->icount.brk++; + if (uart_handle_break(port)) continue; } else if (sts & MAX310X_LSR_RXPAR_BIT) - s->port.icount.parity++; + port->icount.parity++; else if (sts & MAX310X_LSR_FRERR_BIT) - s->port.icount.frame++; + port->icount.frame++; else if (sts & MAX310X_LSR_RXOVR_BIT) - s->port.icount.overrun++; + port->icount.overrun++; - sts &= s->port.read_status_mask; + sts &= port->read_status_mask; if (sts & MAX310X_LSR_RXBRK_BIT) flag = TTY_BREAK; else if (sts & MAX310X_LSR_RXPAR_BIT) @@ -502,129 +581,129 @@ static void max310x_handle_rx(struct max310x_port *s, unsigned int rxlen) flag = TTY_OVERRUN; } - if (uart_handle_sysrq_char(s->port, ch)) + if (uart_handle_sysrq_char(port, ch)) continue; - if (sts & s->port.ignore_status_mask) + if (sts & port->ignore_status_mask) continue; - uart_insert_char(&s->port, sts, MAX310X_LSR_RXOVR_BIT, - ch, flag); + uart_insert_char(port, sts, MAX310X_LSR_RXOVR_BIT, ch, flag); } - tty_flip_buffer_push(&s->port.state->port); + tty_flip_buffer_push(&port->state->port); } -static void max310x_handle_tx(struct max310x_port *s) +static void max310x_handle_tx(struct uart_port *port) { - struct circ_buf *xmit = &s->port.state->xmit; - unsigned int txlen = 0, to_send; + struct circ_buf *xmit = &port->state->xmit; + unsigned int txlen, to_send; - if (unlikely(s->port.x_char)) { - regmap_write(s->regmap, MAX310X_THR_REG, s->port.x_char); - s->port.icount.tx++; - s->port.x_char = 0; + if (unlikely(port->x_char)) { + max310x_port_write(port, MAX310X_THR_REG, port->x_char); + port->icount.tx++; + port->x_char = 0; return; } - if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port)) + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return; /* Get length of data pending in circular buffer */ to_send = uart_circ_chars_pending(xmit); if (likely(to_send)) { /* Limit to size of TX FIFO */ - regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &txlen); - txlen = MAX310X_FIFO_SIZE - txlen; + txlen = max310x_port_read(port, MAX310X_TXFIFOLVL_REG); + txlen = port->fifosize - txlen; to_send = (to_send > txlen) ? txlen : to_send; - dev_dbg(s->port.dev, "TX Len = %u\n", to_send); - /* Add data to send */ - s->port.icount.tx += to_send; + port->icount.tx += to_send; while (to_send--) { - regmap_write(s->regmap, MAX310X_THR_REG, - xmit->buf[xmit->tail]); + max310x_port_write(port, MAX310X_THR_REG, + xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); }; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(&s->port); + uart_write_wakeup(port); } -static irqreturn_t max310x_ist(int irq, void *dev_id) +static void max310x_port_irq(struct max310x_port *s, int portno) { - struct max310x_port *s = (struct max310x_port *)dev_id; - unsigned int ists = 0, lsr = 0, rxlen = 0; + struct uart_port *port = &s->p[portno].port; - mutex_lock(&s->max310x_mutex); + do { + unsigned int ists, lsr, rxlen; - for (;;) { /* Read IRQ status & RX FIFO level */ - regmap_read(s->regmap, MAX310X_IRQSTS_REG, &ists); - regmap_read(s->regmap, MAX310X_LSR_IRQSTS_REG, &lsr); - regmap_read(s->regmap, MAX310X_RXFIFOLVL_REG, &rxlen); - if (!ists && !(lsr & MAX310X_LSR_RXTO_BIT) && !rxlen) + ists = max310x_port_read(port, MAX310X_IRQSTS_REG); + rxlen = max310x_port_read(port, MAX310X_RXFIFOLVL_REG); + if (!ists && !rxlen) break; - dev_dbg(s->port.dev, "IRQ status: 0x%02x\n", ists); - - if (rxlen) - max310x_handle_rx(s, rxlen); - if (ists & MAX310X_IRQ_TX) - max310x_handle_tx(s); - if (ists & MAX310X_IRQ_CTS_BIT) - uart_handle_cts_change(&s->port, + if (ists & MAX310X_IRQ_CTS_BIT) { + lsr = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG); + uart_handle_cts_change(port, !!(lsr & MAX310X_LSR_CTS_BIT)); - } + } + if (rxlen) + max310x_handle_rx(port, rxlen); + if (ists & MAX310X_IRQ_TXEMPTY_BIT) { + mutex_lock(&s->mutex); + max310x_handle_tx(port); + mutex_unlock(&s->mutex); + } + } while (1); +} - mutex_unlock(&s->max310x_mutex); +static irqreturn_t max310x_ist(int irq, void *dev_id) +{ + struct max310x_port *s = (struct max310x_port *)dev_id; + + if (s->uart.nr > 1) { + do { + unsigned int val = ~0; + + WARN_ON_ONCE(regmap_read(s->regmap, + MAX310X_GLOBALIRQ_REG, &val)); + val = ((1 << s->uart.nr) - 1) & ~val; + if (!val) + break; + max310x_port_irq(s, fls(val) - 1); + } while (1); + } else + max310x_port_irq(s, 0); return IRQ_HANDLED; } static void max310x_wq_proc(struct work_struct *ws) { - struct max310x_port *s = container_of(ws, struct max310x_port, tx_work); + struct max310x_one *one = container_of(ws, struct max310x_one, tx_work); + struct max310x_port *s = dev_get_drvdata(one->port.dev); - mutex_lock(&s->max310x_mutex); - max310x_handle_tx(s); - mutex_unlock(&s->max310x_mutex); + mutex_lock(&s->mutex); + max310x_handle_tx(&one->port); + mutex_unlock(&s->mutex); } static void max310x_start_tx(struct uart_port *port) { - struct max310x_port *s = container_of(port, struct max310x_port, port); + struct max310x_one *one = container_of(port, struct max310x_one, port); - queue_work(s->wq, &s->tx_work); -} - -static void max310x_stop_tx(struct uart_port *port) -{ - /* Do nothing */ -} - -static void max310x_stop_rx(struct uart_port *port) -{ - /* Do nothing */ + if (!work_pending(&one->tx_work)) + schedule_work(&one->tx_work); } static unsigned int max310x_tx_empty(struct uart_port *port) { - unsigned int val = 0; - struct max310x_port *s = container_of(port, struct max310x_port, port); - - mutex_lock(&s->max310x_mutex); - regmap_read(s->regmap, MAX310X_TXFIFOLVL_REG, &val); - mutex_unlock(&s->max310x_mutex); + unsigned int lvl, sts; - return val ? 0 : TIOCSER_TEMT; -} + lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG); + sts = max310x_port_read(port, MAX310X_IRQSTS_REG); -static void max310x_enable_ms(struct uart_port *port) -{ - /* Modem status not supported */ + return ((sts & MAX310X_IRQ_TXEMPTY_BIT) && !lvl) ? TIOCSER_TEMT : 0; } static unsigned int max310x_get_mctrl(struct uart_port *port) @@ -644,28 +723,20 @@ static void max310x_set_mctrl(struct uart_port *port, unsigned int mctrl) static void max310x_break_ctl(struct uart_port *port, int break_state) { - struct max310x_port *s = container_of(port, struct max310x_port, port); - - mutex_lock(&s->max310x_mutex); - regmap_update_bits(s->regmap, MAX310X_LCR_REG, - MAX310X_LCR_TXBREAK_BIT, - break_state ? MAX310X_LCR_TXBREAK_BIT : 0); - mutex_unlock(&s->max310x_mutex); + max310x_port_update(port, MAX310X_LCR_REG, + MAX310X_LCR_TXBREAK_BIT, + break_state ? MAX310X_LCR_TXBREAK_BIT : 0); } static void max310x_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct max310x_port *s = container_of(port, struct max310x_port, port); unsigned int lcr, flow = 0; int baud; - mutex_lock(&s->max310x_mutex); - /* Mask termios capabilities we don't support */ termios->c_cflag &= ~CMSPAR; - termios->c_iflag &= ~IXANY; /* Word size */ switch (termios->c_cflag & CSIZE) { @@ -696,7 +767,7 @@ static void max310x_set_termios(struct uart_port *port, lcr |= MAX310X_LCR_STOPLEN_BIT; /* 2 stops */ /* Update LCR register */ - regmap_write(s->regmap, MAX310X_LCR_REG, lcr); + max310x_port_write(port, MAX310X_LCR_REG, lcr); /* Set read status mask */ port->read_status_mask = MAX310X_LSR_RXOVR_BIT; @@ -717,8 +788,8 @@ static void max310x_set_termios(struct uart_port *port, MAX310X_LSR_RXBRK_BIT; /* Configure flow control */ - regmap_write(s->regmap, MAX310X_XON1_REG, termios->c_cc[VSTART]); - regmap_write(s->regmap, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]); + max310x_port_write(port, MAX310X_XON1_REG, termios->c_cc[VSTART]); + max310x_port_write(port, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]); if (termios->c_cflag & CRTSCTS) flow |= MAX310X_FLOWCTRL_AUTOCTS_BIT | MAX310X_FLOWCTRL_AUTORTS_BIT; @@ -728,7 +799,7 @@ static void max310x_set_termios(struct uart_port *port, if (termios->c_iflag & IXOFF) flow |= MAX310X_FLOWCTRL_SWFLOW1_BIT | MAX310X_FLOWCTRL_SWFLOWEN_BIT; - regmap_write(s->regmap, MAX310X_FLOWCTRL_REG, flow); + max310x_port_write(port, MAX310X_FLOWCTRL_REG, flow); /* Get baud rate generator configuration */ baud = uart_get_baud_rate(port, termios, old, @@ -736,36 +807,30 @@ static void max310x_set_termios(struct uart_port *port, port->uartclk / 4); /* Setup baudrate generator */ - max310x_set_baud(s, baud); + max310x_set_baud(port, baud); /* Update timeout according to new baud rate */ uart_update_timeout(port, termios->c_cflag, baud); - - mutex_unlock(&s->max310x_mutex); } static int max310x_startup(struct uart_port *port) { unsigned int val, line = port->line; - struct max310x_port *s = container_of(port, struct max310x_port, port); + struct max310x_port *s = dev_get_drvdata(port->dev); - if (s->pdata->suspend) - s->pdata->suspend(0); - - mutex_lock(&s->max310x_mutex); + s->devtype->power(port, 1); /* Configure baud rate, 9600 as default */ - max310x_set_baud(s, 9600); + max310x_set_baud(port, 9600); /* Configure LCR register, 8N1 mode by default */ - val = MAX310X_LCR_WORD_LEN_8; - regmap_write(s->regmap, MAX310X_LCR_REG, val); + max310x_port_write(port, MAX310X_LCR_REG, MAX310X_LCR_WORD_LEN_8); /* Configure MODE1 register */ - regmap_update_bits(s->regmap, MAX310X_MODE1_REG, - MAX310X_MODE1_TRNSCVCTRL_BIT, - (s->pdata->uart_flags[line] & MAX310X_AUTO_DIR_CTRL) - ? MAX310X_MODE1_TRNSCVCTRL_BIT : 0); + max310x_port_update(port, MAX310X_MODE1_REG, + MAX310X_MODE1_TRNSCVCTRL_BIT, + (s->pdata->uart_flags[line] & MAX310X_AUTO_DIR_CTRL) + ? MAX310X_MODE1_TRNSCVCTRL_BIT : 0); /* Configure MODE2 register */ val = MAX310X_MODE2_RXEMPTINV_BIT; @@ -776,63 +841,40 @@ static int max310x_startup(struct uart_port *port) /* Reset FIFOs */ val |= MAX310X_MODE2_FIFORST_BIT; - regmap_write(s->regmap, MAX310X_MODE2_REG, val); - - /* Configure FIFO trigger level register */ - /* RX FIFO trigger for 16 words, TX FIFO trigger for 64 words */ - val = MAX310X_FIFOTRIGLVL_RX(16) | MAX310X_FIFOTRIGLVL_TX(64); - regmap_write(s->regmap, MAX310X_FIFOTRIGLVL_REG, val); + max310x_port_write(port, MAX310X_MODE2_REG, val); + max310x_port_update(port, MAX310X_MODE2_REG, + MAX310X_MODE2_FIFORST_BIT, 0); /* Configure flow control levels */ /* Flow control halt level 96, resume level 48 */ - val = MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96); - regmap_write(s->regmap, MAX310X_FLOWLVL_REG, val); - - /* Clear timeout register */ - regmap_write(s->regmap, MAX310X_RXTO_REG, 0); - - /* Configure LSR interrupt enable register */ - /* Enable RX timeout interrupt */ - val = MAX310X_LSR_RXTO_BIT; - regmap_write(s->regmap, MAX310X_LSR_IRQEN_REG, val); + max310x_port_write(port, MAX310X_FLOWLVL_REG, + MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96)); - /* Clear FIFO reset */ - regmap_update_bits(s->regmap, MAX310X_MODE2_REG, - MAX310X_MODE2_FIFORST_BIT, 0); + /* Clear IRQ status register */ + max310x_port_read(port, MAX310X_IRQSTS_REG); - /* Clear IRQ status register by reading it */ - regmap_read(s->regmap, MAX310X_IRQSTS_REG, &val); - - /* Configure interrupt enable register */ - /* Enable CTS change interrupt */ - val = MAX310X_IRQ_CTS_BIT; - /* Enable RX, TX interrupts */ - val |= MAX310X_IRQ_RX | MAX310X_IRQ_TX; - regmap_write(s->regmap, MAX310X_IRQEN_REG, val); - - mutex_unlock(&s->max310x_mutex); + /* Enable RX, TX, CTS change interrupts */ + val = MAX310X_IRQ_RXEMPTY_BIT | MAX310X_IRQ_TXEMPTY_BIT; + max310x_port_write(port, MAX310X_IRQEN_REG, val | MAX310X_IRQ_CTS_BIT); return 0; } static void max310x_shutdown(struct uart_port *port) { - struct max310x_port *s = container_of(port, struct max310x_port, port); + struct max310x_port *s = dev_get_drvdata(port->dev); /* Disable all interrupts */ - mutex_lock(&s->max310x_mutex); - regmap_write(s->regmap, MAX310X_IRQEN_REG, 0); - mutex_unlock(&s->max310x_mutex); + max310x_port_write(port, MAX310X_IRQEN_REG, 0); - if (s->pdata->suspend) - s->pdata->suspend(1); + s->devtype->power(port, 0); } static const char *max310x_type(struct uart_port *port) { - struct max310x_port *s = container_of(port, struct max310x_port, port); + struct max310x_port *s = dev_get_drvdata(port->dev); - return (port->type == PORT_MAX310X) ? s->name : NULL; + return (port->type == PORT_MAX310X) ? s->devtype->name : NULL; } static int max310x_request_port(struct uart_port *port) @@ -841,134 +883,100 @@ static int max310x_request_port(struct uart_port *port) return 0; } -static void max310x_release_port(struct uart_port *port) -{ - /* Do nothing */ -} - static void max310x_config_port(struct uart_port *port, int flags) { if (flags & UART_CONFIG_TYPE) port->type = PORT_MAX310X; } -static int max310x_verify_port(struct uart_port *port, struct serial_struct *ser) +static int max310x_verify_port(struct uart_port *port, struct serial_struct *s) { - if ((ser->type == PORT_UNKNOWN) || (ser->type == PORT_MAX310X)) - return 0; - if (ser->irq == port->irq) - return 0; + if ((s->type != PORT_UNKNOWN) && (s->type != PORT_MAX310X)) + return -EINVAL; + if (s->irq != port->irq) + return -EINVAL; - return -EINVAL; + return 0; } -static struct uart_ops max310x_ops = { +static void max310x_null_void(struct uart_port *port) +{ + /* Do nothing */ +} + +static const struct uart_ops max310x_ops = { .tx_empty = max310x_tx_empty, .set_mctrl = max310x_set_mctrl, .get_mctrl = max310x_get_mctrl, - .stop_tx = max310x_stop_tx, + .stop_tx = max310x_null_void, .start_tx = max310x_start_tx, - .stop_rx = max310x_stop_rx, - .enable_ms = max310x_enable_ms, + .stop_rx = max310x_null_void, + .enable_ms = max310x_null_void, .break_ctl = max310x_break_ctl, .startup = max310x_startup, .shutdown = max310x_shutdown, .set_termios = max310x_set_termios, .type = max310x_type, .request_port = max310x_request_port, - .release_port = max310x_release_port, + .release_port = max310x_null_void, .config_port = max310x_config_port, .verify_port = max310x_verify_port, }; -#ifdef CONFIG_PM_SLEEP - -static int max310x_suspend(struct device *dev) +static int __maybe_unused max310x_suspend(struct spi_device *spi, + pm_message_t state) { - int ret; - struct max310x_port *s = dev_get_drvdata(dev); - - dev_dbg(dev, "Suspend\n"); + struct max310x_port *s = dev_get_drvdata(&spi->dev); + int i; - ret = uart_suspend_port(&s->uart, &s->port); - - mutex_lock(&s->max310x_mutex); - - /* Enable sleep mode */ - regmap_update_bits(s->regmap, MAX310X_MODE1_REG, - MAX310X_MODE1_FORCESLEEP_BIT, - MAX310X_MODE1_FORCESLEEP_BIT); - - mutex_unlock(&s->max310x_mutex); - - if (s->pdata->suspend) - s->pdata->suspend(1); + for (i = 0; i < s->uart.nr; i++) { + uart_suspend_port(&s->uart, &s->p[i].port); + s->devtype->power(&s->p[i].port, 0); + } - return ret; + return 0; } -static int max310x_resume(struct device *dev) +static int __maybe_unused max310x_resume(struct spi_device *spi) { - struct max310x_port *s = dev_get_drvdata(dev); - - dev_dbg(dev, "Resume\n"); - - if (s->pdata->suspend) - s->pdata->suspend(0); - - mutex_lock(&s->max310x_mutex); + struct max310x_port *s = dev_get_drvdata(&spi->dev); + int i; - /* Disable sleep mode */ - regmap_update_bits(s->regmap, MAX310X_MODE1_REG, - MAX310X_MODE1_FORCESLEEP_BIT, - 0); - - max310x_wait_pll(s); - - mutex_unlock(&s->max310x_mutex); + for (i = 0; i < s->uart.nr; i++) { + s->devtype->power(&s->p[i].port, 1); + uart_resume_port(&s->uart, &s->p[i].port); + } - return uart_resume_port(&s->uart, &s->port); + return 0; } -static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume); -#define MAX310X_PM_OPS (&max310x_pm_ops) - -#else -#define MAX310X_PM_OPS NULL -#endif - #ifdef CONFIG_GPIOLIB static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset) { - unsigned int val = 0; + unsigned int val; struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct uart_port *port = &s->p[offset / 4].port; - mutex_lock(&s->max310x_mutex); - regmap_read(s->regmap, MAX310X_GPIODATA_REG, &val); - mutex_unlock(&s->max310x_mutex); + val = max310x_port_read(port, MAX310X_GPIODATA_REG); - return !!((val >> 4) & (1 << offset)); + return !!((val >> 4) & (1 << (offset % 4))); } static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) { struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct uart_port *port = &s->p[offset / 4].port; - mutex_lock(&s->max310x_mutex); - regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ? - 1 << offset : 0); - mutex_unlock(&s->max310x_mutex); + max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4), + value ? 1 << (offset % 4) : 0); } static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct uart_port *port = &s->p[offset / 4].port; - mutex_lock(&s->max310x_mutex); - - regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset, 0); - - mutex_unlock(&s->max310x_mutex); + max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4), 0); return 0; } @@ -977,74 +985,42 @@ static int max310x_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { struct max310x_port *s = container_of(chip, struct max310x_port, gpio); + struct uart_port *port = &s->p[offset / 4].port; - mutex_lock(&s->max310x_mutex); - - regmap_update_bits(s->regmap, MAX310X_GPIOCFG_REG, 1 << offset, - 1 << offset); - regmap_update_bits(s->regmap, MAX310X_GPIODATA_REG, 1 << offset, value ? - 1 << offset : 0); - - mutex_unlock(&s->max310x_mutex); + max310x_port_update(port, MAX310X_GPIODATA_REG, 1 << (offset % 4), + value ? 1 << (offset % 4) : 0); + max310x_port_update(port, MAX310X_GPIOCFG_REG, 1 << (offset % 4), + 1 << (offset % 4)); return 0; } #endif -/* Generic platform data */ -static struct max310x_pdata generic_plat_data = { - .driver_flags = MAX310X_EXT_CLK, - .uart_flags[0] = MAX310X_ECHO_SUPRESS, - .frequency = 26000000, -}; - -static int max310x_probe(struct spi_device *spi) +static int max310x_probe(struct device *dev, int is_spi, + struct max310x_devtype *devtype, int irq) { struct max310x_port *s; - struct device *dev = &spi->dev; - int chiptype = spi_get_device_id(spi)->driver_data; - struct max310x_pdata *pdata = dev->platform_data; - unsigned int val = 0; - int ret; + struct max310x_pdata *pdata = dev_get_platdata(dev); + int i, ret, uartclk; /* Check for IRQ */ - if (spi->irq <= 0) { + if (irq <= 0) { dev_err(dev, "No IRQ specified\n"); return -ENOTSUPP; } + if (!pdata) { + dev_err(dev, "No platform data supplied\n"); + return -EINVAL; + } + /* Alloc port structure */ - s = devm_kzalloc(dev, sizeof(struct max310x_port), GFP_KERNEL); + s = devm_kzalloc(dev, sizeof(*s) + + sizeof(struct max310x_one) * devtype->nr, GFP_KERNEL); if (!s) { dev_err(dev, "Error allocating port structure\n"); return -ENOMEM; } - dev_set_drvdata(dev, s); - - if (!pdata) { - dev_warn(dev, "No platform data supplied, using defaults\n"); - pdata = &generic_plat_data; - } - s->pdata = pdata; - - /* Individual chip settings */ - switch (chiptype) { - case MAX310X_TYPE_MAX3107: - s->name = "MAX3107"; - s->nr_gpio = 4; - s->uart.nr = 1; - s->regcfg.max_register = 0x1f; - break; - case MAX310X_TYPE_MAX3108: - s->name = "MAX3108"; - s->nr_gpio = 4; - s->uart.nr = 1; - s->regcfg.max_register = 0x1e; - break; - default: - dev_err(dev, "Unsupported chip type %i\n", chiptype); - return -ENOTSUPP; - } /* Check input frequency */ if ((pdata->driver_flags & MAX310X_EXT_CLK) && @@ -1055,13 +1031,11 @@ static int max310x_probe(struct spi_device *spi) ((pdata->frequency < 1000000) || (pdata->frequency > 4000000))) goto err_freq; - mutex_init(&s->max310x_mutex); + s->pdata = pdata; + s->devtype = devtype; + dev_set_drvdata(dev, s); - /* Setup SPI bus */ - spi->mode = SPI_MODE_0; - spi->bits_per_word = 8; - spi->max_speed_hz = 26000000; - spi_setup(spi); + mutex_init(&s->mutex); /* Setup regmap */ s->regcfg.reg_bits = 8; @@ -1069,109 +1043,100 @@ static int max310x_probe(struct spi_device *spi) s->regcfg.read_flag_mask = 0x00; s->regcfg.write_flag_mask = 0x80; s->regcfg.cache_type = REGCACHE_RBTREE; - s->regcfg.writeable_reg = max3107_8_reg_writeable; + s->regcfg.writeable_reg = max310x_reg_writeable; s->regcfg.volatile_reg = max310x_reg_volatile; s->regcfg.precious_reg = max310x_reg_precious; - s->regmap = devm_regmap_init_spi(spi, &s->regcfg); + s->regcfg.max_register = devtype->nr * 0x20 - 1; + + if (IS_ENABLED(CONFIG_SPI_MASTER) && is_spi) { + struct spi_device *spi = to_spi_device(dev); + + s->regmap = devm_regmap_init_spi(spi, &s->regcfg); + } else + return -ENOTSUPP; + if (IS_ERR(s->regmap)) { - ret = PTR_ERR(s->regmap); dev_err(dev, "Failed to initialize register map\n"); - goto err_out; - } - - /* Reset chip & check SPI function */ - ret = regmap_write(s->regmap, MAX310X_MODE2_REG, MAX310X_MODE2_RST_BIT); - if (ret) { - dev_err(dev, "SPI transfer failed\n"); - goto err_out; - } - /* Clear chip reset */ - regmap_write(s->regmap, MAX310X_MODE2_REG, 0); - - switch (chiptype) { - case MAX310X_TYPE_MAX3107: - /* Check REV ID to ensure we are talking to what we expect */ - regmap_read(s->regmap, MAX3107_REVID_REG, &val); - if (((val & MAX3107_REV_MASK) != MAX3107_REV_ID)) { - dev_err(dev, "%s ID 0x%02x does not match\n", - s->name, val); - ret = -ENODEV; - goto err_out; - } - break; - case MAX310X_TYPE_MAX3108: - /* MAX3108 have not REV ID register, we just check default value - * from clocksource register to make sure everything works. - */ - regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val); - if (val != (MAX310X_CLKSRC_EXTCLK_BIT | - MAX310X_CLKSRC_PLLBYP_BIT)) { - dev_err(dev, "%s not present\n", s->name); - ret = -ENODEV; - goto err_out; - } - break; + return PTR_ERR(s->regmap); } /* Board specific configure */ - if (pdata->init) - pdata->init(); - if (pdata->suspend) - pdata->suspend(0); - - /* Calculate referecne clock */ - s->uartclk = max310x_set_ref_clk(s); - - /* Disable all interrupts */ - regmap_write(s->regmap, MAX310X_IRQEN_REG, 0); - - /* Setup MODE1 register */ - val = MAX310X_MODE1_IRQSEL_BIT; /* Enable IRQ pin */ - if (pdata->driver_flags & MAX310X_AUTOSLEEP) - val = MAX310X_MODE1_AUTOSLEEP_BIT; - regmap_write(s->regmap, MAX310X_MODE1_REG, val); - - /* Setup interrupt */ - ret = devm_request_threaded_irq(dev, spi->irq, NULL, max310x_ist, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(dev), s); - if (ret) { - dev_err(dev, "Unable to reguest IRQ %i\n", spi->irq); - goto err_out; + if (s->pdata->init) + s->pdata->init(); + + /* Check device to ensure we are talking to what we expect */ + ret = devtype->detect(dev); + if (ret) + return ret; + + for (i = 0; i < devtype->nr; i++) { + unsigned int offs = i << 5; + + /* Reset port */ + regmap_write(s->regmap, MAX310X_MODE2_REG + offs, + MAX310X_MODE2_RST_BIT); + /* Clear port reset */ + regmap_write(s->regmap, MAX310X_MODE2_REG + offs, 0); + + /* Wait for port startup */ + do { + regmap_read(s->regmap, + MAX310X_BRGDIVLSB_REG + offs, &ret); + } while (ret != 0x01); + + regmap_update_bits(s->regmap, MAX310X_MODE1_REG + offs, + MAX310X_MODE1_AUTOSLEEP_BIT, + MAX310X_MODE1_AUTOSLEEP_BIT); } + uartclk = max310x_set_ref_clk(s); + dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk); + /* Register UART driver */ s->uart.owner = THIS_MODULE; - s->uart.driver_name = dev_name(dev); s->uart.dev_name = "ttyMAX"; s->uart.major = MAX310X_MAJOR; s->uart.minor = MAX310X_MINOR; + s->uart.nr = devtype->nr; ret = uart_register_driver(&s->uart); if (ret) { dev_err(dev, "Registering UART driver failed\n"); - goto err_out; + return ret; } - /* Initialize workqueue for start TX */ - s->wq = create_freezable_workqueue(dev_name(dev)); - INIT_WORK(&s->tx_work, max310x_wq_proc); - - /* Initialize UART port data */ - s->port.line = 0; - s->port.dev = dev; - s->port.irq = spi->irq; - s->port.type = PORT_MAX310X; - s->port.fifosize = MAX310X_FIFO_SIZE; - s->port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE; - s->port.iotype = UPIO_PORT; - s->port.membase = (void __iomem *)0xffffffff; /* Bogus value */ - s->port.uartclk = s->uartclk; - s->port.ops = &max310x_ops; - uart_add_one_port(&s->uart, &s->port); + for (i = 0; i < devtype->nr; i++) { + /* Initialize port data */ + s->p[i].port.line = i; + s->p[i].port.dev = dev; + s->p[i].port.irq = irq; + s->p[i].port.type = PORT_MAX310X; + s->p[i].port.fifosize = MAX310X_FIFO_SIZE; + s->p[i].port.flags = UPF_SKIP_TEST | UPF_FIXED_TYPE | + UPF_LOW_LATENCY; + s->p[i].port.iotype = UPIO_PORT; + s->p[i].port.iobase = i * 0x20; + s->p[i].port.membase = (void __iomem *)~0; + s->p[i].port.uartclk = uartclk; + s->p[i].port.ops = &max310x_ops; + /* Disable all interrupts */ + max310x_port_write(&s->p[i].port, MAX310X_IRQEN_REG, 0); + /* Clear IRQ status register */ + max310x_port_read(&s->p[i].port, MAX310X_IRQSTS_REG); + /* Enable IRQ pin */ + max310x_port_update(&s->p[i].port, MAX310X_MODE1_REG, + MAX310X_MODE1_IRQSEL_BIT, + MAX310X_MODE1_IRQSEL_BIT); + /* Initialize queue for start TX */ + INIT_WORK(&s->p[i].tx_work, max310x_wq_proc); + /* Register port */ + uart_add_one_port(&s->uart, &s->p[i].port); + /* Go to suspend mode */ + devtype->power(&s->p[i].port, 0); + } #ifdef CONFIG_GPIOLIB /* Setup GPIO cotroller */ - if (pdata->gpio_base) { + if (s->pdata->gpio_base) { s->gpio.owner = THIS_MODULE; s->gpio.dev = dev; s->gpio.label = dev_name(dev); @@ -1179,86 +1144,105 @@ static int max310x_probe(struct spi_device *spi) s->gpio.get = max310x_gpio_get; s->gpio.direction_output= max310x_gpio_direction_output; s->gpio.set = max310x_gpio_set; - s->gpio.base = pdata->gpio_base; - s->gpio.ngpio = s->nr_gpio; + s->gpio.base = s->pdata->gpio_base; + s->gpio.ngpio = devtype->nr * 4; s->gpio.can_sleep = 1; - if (gpiochip_add(&s->gpio)) { - /* Indicate that we should not call gpiochip_remove */ - s->gpio.base = 0; - } + if (!gpiochip_add(&s->gpio)) + s->gpio_used = 1; } else dev_info(dev, "GPIO support not enabled\n"); #endif - /* Go to suspend mode */ - if (pdata->suspend) - pdata->suspend(1); + /* Setup interrupt */ + ret = devm_request_threaded_irq(dev, irq, NULL, max310x_ist, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(dev), s); + if (ret) { + dev_err(dev, "Unable to reguest IRQ %i\n", irq); +#ifdef CONFIG_GPIOLIB + if (s->gpio_used) + WARN_ON(gpiochip_remove(&s->gpio)); +#endif + } - return 0; + return ret; err_freq: dev_err(dev, "Frequency parameter incorrect\n"); - ret = -EINVAL; - -err_out: - dev_set_drvdata(dev, NULL); - - return ret; + return -EINVAL; } -static int max310x_remove(struct spi_device *spi) +static int max310x_remove(struct device *dev) { - struct device *dev = &spi->dev; struct max310x_port *s = dev_get_drvdata(dev); - int ret = 0; - - dev_dbg(dev, "Removing port\n"); - - devm_free_irq(dev, s->port.irq, s); + int i, ret = 0; - destroy_workqueue(s->wq); - - uart_remove_one_port(&s->uart, &s->port); + for (i = 0; i < s->uart.nr; i++) { + cancel_work_sync(&s->p[i].tx_work); + uart_remove_one_port(&s->uart, &s->p[i].port); + s->devtype->power(&s->p[i].port, 0); + } uart_unregister_driver(&s->uart); #ifdef CONFIG_GPIOLIB - if (s->pdata->gpio_base) { + if (s->gpio_used) ret = gpiochip_remove(&s->gpio); - if (ret) - dev_err(dev, "Failed to remove gpio chip: %d\n", ret); - } #endif - dev_set_drvdata(dev, NULL); - - if (s->pdata->suspend) - s->pdata->suspend(1); if (s->pdata->exit) s->pdata->exit(); return ret; } +#ifdef CONFIG_SPI_MASTER +static int max310x_spi_probe(struct spi_device *spi) +{ + struct max310x_devtype *devtype = + (struct max310x_devtype *)spi_get_device_id(spi)->driver_data; + int ret; + + /* Setup SPI bus */ + spi->bits_per_word = 8; + spi->mode = spi->mode ? : SPI_MODE_0; + spi->max_speed_hz = spi->max_speed_hz ? : 26000000; + ret = spi_setup(spi); + if (ret) { + dev_err(&spi->dev, "SPI setup failed\n"); + return ret; + } + + return max310x_probe(&spi->dev, 1, devtype, spi->irq); +} + +static int max310x_spi_remove(struct spi_device *spi) +{ + return max310x_remove(&spi->dev); +} + +static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume); + static const struct spi_device_id max310x_id_table[] = { - { "max3107", MAX310X_TYPE_MAX3107 }, - { "max3108", MAX310X_TYPE_MAX3108 }, + { "max3107", (kernel_ulong_t)&max3107_devtype, }, + { "max3108", (kernel_ulong_t)&max3108_devtype, }, { } }; MODULE_DEVICE_TABLE(spi, max310x_id_table); -static struct spi_driver max310x_driver = { +static struct spi_driver max310x_uart_driver = { .driver = { - .name = "max310x", + .name = MAX310X_NAME, .owner = THIS_MODULE, - .pm = MAX310X_PM_OPS, + .pm = &max310x_pm_ops, }, - .probe = max310x_probe, - .remove = max310x_remove, + .probe = max310x_spi_probe, + .remove = max310x_spi_remove, .id_table = max310x_id_table, }; -module_spi_driver(max310x_driver); +module_spi_driver(max310x_uart_driver); +#endif -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); MODULE_AUTHOR("Alexander Shiyan "); MODULE_DESCRIPTION("MAX310X serial driver"); diff --git a/include/linux/platform_data/max310x.h b/include/linux/platform_data/max310x.h index 91648bf5fc5c..1aec0b620ac3 100644 --- a/include/linux/platform_data/max310x.h +++ b/include/linux/platform_data/max310x.h @@ -42,9 +42,8 @@ /* MAX310X platform data structure */ struct max310x_pdata { /* Flags global to driver */ - const u8 driver_flags:2; + const u8 driver_flags; #define MAX310X_EXT_CLK (0x00000001) /* External clock enable */ -#define MAX310X_AUTOSLEEP (0x00000002) /* Enable AutoSleep mode */ /* Flags global to UART port */ const u8 uart_flags[MAX310X_MAX_UARTS]; #define MAX310X_LOOPBACK (0x00000001) /* Loopback mode enable */ @@ -60,8 +59,6 @@ struct max310x_pdata { void (*init)(void); /* Called before finish */ void (*exit)(void); - /* Suspend callback */ - void (*suspend)(int do_suspend); }; #endif -- cgit v1.2.3-71-gd317 From 21fc509f1194c2fa06eff4c72238210089c29453 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 29 Jun 2013 10:44:18 +0400 Subject: serial: max310x: Add MAX3109 support This patch adds support for MAX3109 (advanced dual universal asynchronous receiver-transmitter) into max310x driver. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- drivers/tty/serial/max310x.c | 35 +++++++++++++++++++++++++++++++---- include/linux/platform_data/max310x.h | 4 ++-- 3 files changed, 34 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 25772c15276d..8e1a9c53ad7f 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -297,7 +297,7 @@ config SERIAL_MAX310X default n help This selects support for an advanced UART from Maxim (Dallas). - Supported ICs are MAX3107, MAX3108. + Supported ICs are MAX3107, MAX3108, MAX3109. Each IC contains 128 words each of receive and transmit FIFO that can be controlled through I2C or high-speed SPI. diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 4620289e9e49..a6c46427363b 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1,5 +1,5 @@ /* - * Maxim (Dallas) MAX3107/8 serial driver + * Maxim (Dallas) MAX3107/8/9 serial driver * * Copyright (C) 2012-2013 Alexander Shiyan * @@ -13,9 +13,6 @@ * (at your option) any later version. */ -/* TODO: MAX3109 support (Dual) */ -/* TODO: MAX14830 support (Quad) */ - #include #include #include @@ -269,6 +266,9 @@ /* MAX3107 specific */ #define MAX3107_REV_ID (0xa0) +/* MAX3109 specific */ +#define MAX3109_REV_ID (0xc0) + struct max310x_devtype { char name[9]; int nr; @@ -359,6 +359,25 @@ static int max3108_detect(struct device *dev) return 0; } +static int max3109_detect(struct device *dev) +{ + struct max310x_port *s = dev_get_drvdata(dev); + unsigned int val = 0; + int ret; + + ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val); + if (ret) + return ret; + + if (((val & MAX310x_REV_MASK) != MAX3109_REV_ID)) { + dev_err(dev, + "%s ID 0x%02x does not match\n", s->devtype->name, val); + return -ENODEV; + } + + return 0; +} + static void max310x_power(struct uart_port *port, int on) { max310x_port_update(port, MAX310X_MODE1_REG, @@ -382,6 +401,13 @@ static const struct max310x_devtype max3108_devtype = { .power = max310x_power, }; +static const struct max310x_devtype max3109_devtype = { + .name = "MAX3109", + .nr = 2, + .detect = max3109_detect, + .power = max310x_power, +}; + static bool max310x_reg_writeable(struct device *dev, unsigned int reg) { switch (reg & 0x1f) { @@ -1226,6 +1252,7 @@ static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume); static const struct spi_device_id max310x_id_table[] = { { "max3107", (kernel_ulong_t)&max3107_devtype, }, { "max3108", (kernel_ulong_t)&max3108_devtype, }, + { "max3109", (kernel_ulong_t)&max3109_devtype, }, { } }; MODULE_DEVICE_TABLE(spi, max310x_id_table); diff --git a/include/linux/platform_data/max310x.h b/include/linux/platform_data/max310x.h index 1aec0b620ac3..4c128eda26ba 100644 --- a/include/linux/platform_data/max310x.h +++ b/include/linux/platform_data/max310x.h @@ -1,5 +1,5 @@ /* - * Maxim (Dallas) MAX3107/8 serial driver + * Maxim (Dallas) MAX3107/8/9 serial driver * * Copyright (C) 2012 Alexander Shiyan * @@ -37,7 +37,7 @@ * }; */ -#define MAX310X_MAX_UARTS 1 +#define MAX310X_MAX_UARTS 2 /* MAX310X platform data structure */ struct max310x_pdata { -- cgit v1.2.3-71-gd317 From 003236d9ac4d02721867e47f7ad4371ab7f74689 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Sat, 29 Jun 2013 10:44:19 +0400 Subject: serial: max310x: Add MAX14830 support This patch adds support for MAX14830 (advanced quad universal asynchronous receiver-transmitter) into max310x driver. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- drivers/tty/serial/max310x.c | 45 ++++++++++++++++++++++++++++++++++- include/linux/platform_data/max310x.h | 4 ++-- 3 files changed, 47 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 8e1a9c53ad7f..bc486deeeae5 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -297,7 +297,7 @@ config SERIAL_MAX310X default n help This selects support for an advanced UART from Maxim (Dallas). - Supported ICs are MAX3107, MAX3108, MAX3109. + Supported ICs are MAX3107, MAX3108, MAX3109, MAX14830. Each IC contains 128 words each of receive and transmit FIFO that can be controlled through I2C or high-speed SPI. diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index a6c46427363b..4ab5b272a593 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -1,5 +1,5 @@ /* - * Maxim (Dallas) MAX3107/8/9 serial driver + * Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver * * Copyright (C) 2012-2013 Alexander Shiyan * @@ -269,6 +269,10 @@ /* MAX3109 specific */ #define MAX3109_REV_ID (0xc0) +/* MAX14830 specific */ +#define MAX14830_BRGCFG_CLKDIS_BIT (1 << 6) /* Clock Disable */ +#define MAX14830_REV_ID (0xb0) + struct max310x_devtype { char name[9]; int nr; @@ -387,6 +391,37 @@ static void max310x_power(struct uart_port *port, int on) msleep(50); } +static int max14830_detect(struct device *dev) +{ + struct max310x_port *s = dev_get_drvdata(dev); + unsigned int val = 0; + int ret; + + ret = regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, + MAX310X_EXTREG_ENBL); + if (ret) + return ret; + + regmap_read(s->regmap, MAX310X_REVID_EXTREG, &val); + regmap_write(s->regmap, MAX310X_GLOBALCMD_REG, MAX310X_EXTREG_DSBL); + if (((val & MAX310x_REV_MASK) != MAX14830_REV_ID)) { + dev_err(dev, + "%s ID 0x%02x does not match\n", s->devtype->name, val); + return -ENODEV; + } + + return 0; +} + +static void max14830_power(struct uart_port *port, int on) +{ + max310x_port_update(port, MAX310X_BRGCFG_REG, + MAX14830_BRGCFG_CLKDIS_BIT, + on ? 0 : MAX14830_BRGCFG_CLKDIS_BIT); + if (on) + msleep(50); +} + static const struct max310x_devtype max3107_devtype = { .name = "MAX3107", .nr = 1, @@ -408,6 +443,13 @@ static const struct max310x_devtype max3109_devtype = { .power = max310x_power, }; +static const struct max310x_devtype max14830_devtype = { + .name = "MAX14830", + .nr = 4, + .detect = max14830_detect, + .power = max14830_power, +}; + static bool max310x_reg_writeable(struct device *dev, unsigned int reg) { switch (reg & 0x1f) { @@ -1253,6 +1295,7 @@ static const struct spi_device_id max310x_id_table[] = { { "max3107", (kernel_ulong_t)&max3107_devtype, }, { "max3108", (kernel_ulong_t)&max3108_devtype, }, { "max3109", (kernel_ulong_t)&max3109_devtype, }, + { "max14830", (kernel_ulong_t)&max14830_devtype, }, { } }; MODULE_DEVICE_TABLE(spi, max310x_id_table); diff --git a/include/linux/platform_data/max310x.h b/include/linux/platform_data/max310x.h index 4c128eda26ba..dd11dcd1a184 100644 --- a/include/linux/platform_data/max310x.h +++ b/include/linux/platform_data/max310x.h @@ -1,5 +1,5 @@ /* - * Maxim (Dallas) MAX3107/8/9 serial driver + * Maxim (Dallas) MAX3107/8/9, MAX14830 serial driver * * Copyright (C) 2012 Alexander Shiyan * @@ -37,7 +37,7 @@ * }; */ -#define MAX310X_MAX_UARTS 2 +#define MAX310X_MAX_UARTS 4 /* MAX310X platform data structure */ struct max310x_pdata { -- cgit v1.2.3-71-gd317 From c4415084218c68c5ee2fc583431e89a78d896b19 Mon Sep 17 00:00:00 2001 From: Dmitry Fink Date: Mon, 8 Jul 2013 13:04:44 +0300 Subject: OMAP: UART: Keep the TX fifo full when possible Current logic results in interrupt storm since the fifo is constantly below the threshold level. Change the logic to fill all the available spaces in the fifo as long as we have data to minimize the possibilty of underflow and elimiate excessive interrupts. Signed-off-by: Dmitry Fink Signed-off-by: Alexander Savchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 3 ++- include/uapi/linux/serial_reg.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 33c758e09d86..9271a1dceec3 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -315,7 +315,8 @@ static void transmit_chars(struct uart_omap_port *up, unsigned int lsr) serial_omap_stop_tx(&up->port); return; } - count = up->port.fifosize / 4; + count = up->port.fifosize - + (serial_in(up, UART_OMAP_TXFIFO_LVL) & 0xFF); do { serial_out(up, UART_TX, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index e6322605b138..97c26beae605 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -366,6 +366,7 @@ #define UART_OMAP_MDR1_FIR_MODE 0x05 /* FIR mode */ #define UART_OMAP_MDR1_CIR_MODE 0x06 /* CIR mode */ #define UART_OMAP_MDR1_DISABLE 0x07 /* Disable (default state) */ +#define UART_OMAP_TXFIFO_LVL 0x1A /* TX FIFO fullness */ /* * These are definitions for the Exar XR17V35X and XR17(C|D)15X -- cgit v1.2.3-71-gd317 From c4b058560762ec7ffe159b668fc47a8b7e271949 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 15 Jul 2013 12:39:23 +0100 Subject: serial:st-asc: Add ST ASC driver. This patch adds support to ASC (asynchronous serial controller) driver, which is basically a standard serial driver. This IP is common across all the ST parts for settop box platforms. ASC is embedded in ST COMMS IP block. It supports Rx & Tx functionality. It support all industry standard baud rates. Signed-off-by: Srinivas Kandagatla CC: Stephen Gallimore CC: Stuart Menefy CC: Arnd Bergmann CC: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/tty/serial/st-asc.txt | 18 + drivers/tty/serial/Kconfig | 16 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/st-asc.c | 937 +++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 5 files changed, 975 insertions(+) create mode 100644 Documentation/devicetree/bindings/tty/serial/st-asc.txt create mode 100644 drivers/tty/serial/st-asc.c (limited to 'include') diff --git a/Documentation/devicetree/bindings/tty/serial/st-asc.txt b/Documentation/devicetree/bindings/tty/serial/st-asc.txt new file mode 100644 index 000000000000..75d877f5968f --- /dev/null +++ b/Documentation/devicetree/bindings/tty/serial/st-asc.txt @@ -0,0 +1,18 @@ +*st-asc(Serial Port) + +Required properties: +- compatible : Should be "st,asc". +- reg, reg-names, interrupts, interrupt-names : Standard way to define device + resources with names. look in + Documentation/devicetree/bindings/resource-names.txt + +Optional properties: +- st,hw-flow-ctrl bool flag to enable hardware flow control. +- st,force-m1 bool flat to force asc to be in Mode-1 recommeded + for high bit rates (above 19.2K) +Example: +serial@fe440000{ + compatible = "st,asc"; + reg = <0xfe440000 0x2c>; + interrupts = <0 209 0>; +}; diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 6e73781bea63..bdf5c0e44b68 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1497,6 +1497,22 @@ config SERIAL_FSL_LPUART_CONSOLE If you have enabled the lpuart serial port on the Freescale SoCs, you can make it the console by answering Y to this option. +config SERIAL_ST_ASC + tristate "ST ASC serial port support" + select SERIAL_CORE + help + This driver is for the on-chip Asychronous Serial Controller on + STMicroelectronics STi SoCs. + ASC is embedded in ST COMMS IP block. It supports Rx & Tx functionality. + It support all industry standard baud rates. + + If unsure, say N. + +config SERIAL_ST_ASC_CONSOLE + bool "Support for console on ST ASC" + depends on SERIAL_ST_ASC=y + select SERIAL_CORE_CONSOLE + endmenu endif # TTY diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index cf650f0cd6e4..47b679c547e9 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o +obj-$(CONFIG_SERIAL_ST_ASC) += st-asc.o obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c new file mode 100644 index 000000000000..183879825695 --- /dev/null +++ b/drivers/tty/serial/st-asc.c @@ -0,0 +1,937 @@ +/* + * st-asc.c: ST Asynchronous serial controller (ASC) driver + * + * Copyright (C) 2003-2013 STMicroelectronics (R&D) Limited + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#if defined(CONFIG_SERIAL_ST_ASC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "st-asc" +#define ASC_SERIAL_NAME "ttyAS" +#define ASC_FIFO_SIZE 16 +#define ASC_MAX_PORTS 8 + +struct asc_port { + struct uart_port port; + struct clk *clk; + unsigned int hw_flow_control:1; + unsigned int force_m1:1; +}; + +static struct asc_port asc_ports[ASC_MAX_PORTS]; +static struct uart_driver asc_uart_driver; + +/*---- UART Register definitions ------------------------------*/ + +/* Register offsets */ + +#define ASC_BAUDRATE 0x00 +#define ASC_TXBUF 0x04 +#define ASC_RXBUF 0x08 +#define ASC_CTL 0x0C +#define ASC_INTEN 0x10 +#define ASC_STA 0x14 +#define ASC_GUARDTIME 0x18 +#define ASC_TIMEOUT 0x1C +#define ASC_TXRESET 0x20 +#define ASC_RXRESET 0x24 +#define ASC_RETRIES 0x28 + +/* ASC_RXBUF */ +#define ASC_RXBUF_PE 0x100 +#define ASC_RXBUF_FE 0x200 +/** + * Some of status comes from higher bits of the character and some come from + * the status register. Combining both of them in to single status using dummy + * bits. + */ +#define ASC_RXBUF_DUMMY_RX 0x10000 +#define ASC_RXBUF_DUMMY_BE 0x20000 +#define ASC_RXBUF_DUMMY_OE 0x40000 + +/* ASC_CTL */ + +#define ASC_CTL_MODE_MSK 0x0007 +#define ASC_CTL_MODE_8BIT 0x0001 +#define ASC_CTL_MODE_7BIT_PAR 0x0003 +#define ASC_CTL_MODE_9BIT 0x0004 +#define ASC_CTL_MODE_8BIT_WKUP 0x0005 +#define ASC_CTL_MODE_8BIT_PAR 0x0007 +#define ASC_CTL_STOP_MSK 0x0018 +#define ASC_CTL_STOP_HALFBIT 0x0000 +#define ASC_CTL_STOP_1BIT 0x0008 +#define ASC_CTL_STOP_1_HALFBIT 0x0010 +#define ASC_CTL_STOP_2BIT 0x0018 +#define ASC_CTL_PARITYODD 0x0020 +#define ASC_CTL_LOOPBACK 0x0040 +#define ASC_CTL_RUN 0x0080 +#define ASC_CTL_RXENABLE 0x0100 +#define ASC_CTL_SCENABLE 0x0200 +#define ASC_CTL_FIFOENABLE 0x0400 +#define ASC_CTL_CTSENABLE 0x0800 +#define ASC_CTL_BAUDMODE 0x1000 + +/* ASC_GUARDTIME */ + +#define ASC_GUARDTIME_MSK 0x00FF + +/* ASC_INTEN */ + +#define ASC_INTEN_RBE 0x0001 +#define ASC_INTEN_TE 0x0002 +#define ASC_INTEN_THE 0x0004 +#define ASC_INTEN_PE 0x0008 +#define ASC_INTEN_FE 0x0010 +#define ASC_INTEN_OE 0x0020 +#define ASC_INTEN_TNE 0x0040 +#define ASC_INTEN_TOI 0x0080 +#define ASC_INTEN_RHF 0x0100 + +/* ASC_RETRIES */ + +#define ASC_RETRIES_MSK 0x00FF + +/* ASC_RXBUF */ + +#define ASC_RXBUF_MSK 0x03FF + +/* ASC_STA */ + +#define ASC_STA_RBF 0x0001 +#define ASC_STA_TE 0x0002 +#define ASC_STA_THE 0x0004 +#define ASC_STA_PE 0x0008 +#define ASC_STA_FE 0x0010 +#define ASC_STA_OE 0x0020 +#define ASC_STA_TNE 0x0040 +#define ASC_STA_TOI 0x0080 +#define ASC_STA_RHF 0x0100 +#define ASC_STA_TF 0x0200 +#define ASC_STA_NKD 0x0400 + +/* ASC_TIMEOUT */ + +#define ASC_TIMEOUT_MSK 0x00FF + +/* ASC_TXBUF */ + +#define ASC_TXBUF_MSK 0x01FF + +/*---- Inline function definitions ---------------------------*/ + +static inline struct asc_port *to_asc_port(struct uart_port *port) +{ + return container_of(port, struct asc_port, port); +} + +static inline u32 asc_in(struct uart_port *port, u32 offset) +{ + return readl(port->membase + offset); +} + +static inline void asc_out(struct uart_port *port, u32 offset, u32 value) +{ + writel(value, port->membase + offset); +} + +/* + * Some simple utility functions to enable and disable interrupts. + * Note that these need to be called with interrupts disabled. + */ +static inline void asc_disable_tx_interrupts(struct uart_port *port) +{ + u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_THE; + asc_out(port, ASC_INTEN, intenable); + (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */ +} + +static inline void asc_enable_tx_interrupts(struct uart_port *port) +{ + u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_THE; + asc_out(port, ASC_INTEN, intenable); +} + +static inline void asc_disable_rx_interrupts(struct uart_port *port) +{ + u32 intenable = asc_in(port, ASC_INTEN) & ~ASC_INTEN_RBE; + asc_out(port, ASC_INTEN, intenable); + (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */ +} + +static inline void asc_enable_rx_interrupts(struct uart_port *port) +{ + u32 intenable = asc_in(port, ASC_INTEN) | ASC_INTEN_RBE; + asc_out(port, ASC_INTEN, intenable); +} + +static inline u32 asc_txfifo_is_empty(struct uart_port *port) +{ + return asc_in(port, ASC_STA) & ASC_STA_TE; +} + +static inline int asc_txfifo_is_full(struct uart_port *port) +{ + return asc_in(port, ASC_STA) & ASC_STA_TF; +} + +static inline const char *asc_port_name(struct uart_port *port) +{ + return to_platform_device(port->dev)->name; +} + +/*----------------------------------------------------------------------*/ + +/* + * This section contains code to support the use of the ASC as a + * generic serial port. + */ + +static inline unsigned asc_hw_txroom(struct uart_port *port) +{ + u32 status = asc_in(port, ASC_STA); + + if (status & ASC_STA_THE) + return port->fifosize / 2; + else if (!(status & ASC_STA_TF)) + return 1; + + return 0; +} + +/* + * Start transmitting chars. + * This is called from both interrupt and task level. + * Either way interrupts are disabled. + */ +static void asc_transmit_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + int txroom; + unsigned char c; + + txroom = asc_hw_txroom(port); + + if ((txroom != 0) && port->x_char) { + c = port->x_char; + port->x_char = 0; + asc_out(port, ASC_TXBUF, c); + port->icount.tx++; + txroom = asc_hw_txroom(port); + } + + if (uart_tx_stopped(port)) { + /* + * We should try and stop the hardware here, but I + * don't think the ASC has any way to do that. + */ + asc_disable_tx_interrupts(port); + return; + } + + if (uart_circ_empty(xmit)) { + asc_disable_tx_interrupts(port); + return; + } + + if (txroom == 0) + return; + + do { + c = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + asc_out(port, ASC_TXBUF, c); + port->icount.tx++; + txroom--; + } while ((txroom > 0) && (!uart_circ_empty(xmit))); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + asc_disable_tx_interrupts(port); +} + +static void asc_receive_chars(struct uart_port *port) +{ + struct tty_port *tport = &port->state->port; + unsigned long status; + unsigned long c = 0; + char flag; + + if (port->irq_wake) + pm_wakeup_event(tport->tty->dev, 0); + + while ((status = asc_in(port, ASC_STA)) & ASC_STA_RBF) { + c = asc_in(port, ASC_RXBUF) | ASC_RXBUF_DUMMY_RX; + flag = TTY_NORMAL; + port->icount.rx++; + + if ((c & (ASC_RXBUF_FE | ASC_RXBUF_PE)) || + status & ASC_STA_OE) { + + if (c & ASC_RXBUF_FE) { + if (c == ASC_RXBUF_FE) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + c |= ASC_RXBUF_DUMMY_BE; + } else { + port->icount.frame++; + } + } else if (c & ASC_RXBUF_PE) { + port->icount.parity++; + } + /* + * Reading any data from the RX FIFO clears the + * overflow error condition. + */ + if (status & ASC_STA_OE) { + port->icount.overrun++; + c |= ASC_RXBUF_DUMMY_OE; + } + + c &= port->read_status_mask; + + if (c & ASC_RXBUF_DUMMY_BE) + flag = TTY_BREAK; + else if (c & ASC_RXBUF_PE) + flag = TTY_PARITY; + else if (c & ASC_RXBUF_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, c)) + continue; + + uart_insert_char(port, c, ASC_RXBUF_DUMMY_OE, c & 0xff, flag); + } + + /* Tell the rest of the system the news. New characters! */ + tty_flip_buffer_push(tport); +} + +static irqreturn_t asc_interrupt(int irq, void *ptr) +{ + struct uart_port *port = ptr; + u32 status; + + spin_lock(&port->lock); + + status = asc_in(port, ASC_STA); + + if (status & ASC_STA_RBF) { + /* Receive FIFO not empty */ + asc_receive_chars(port); + } + + if ((status & ASC_STA_THE) && + (asc_in(port, ASC_INTEN) & ASC_INTEN_THE)) { + /* Transmitter FIFO at least half empty */ + asc_transmit_chars(port); + } + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +/* + * UART Functions + */ + +static unsigned int asc_tx_empty(struct uart_port *port) +{ + return asc_txfifo_is_empty(port) ? TIOCSER_TEMT : 0; +} + +static void asc_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* + * This routine is used for seting signals of: DTR, DCD, CTS/RTS + * We use ASC's hardware for CTS/RTS, so don't need any for that. + * Some boards have DTR and DCD implemented using PIO pins, + * code to do this should be hooked in here. + */ +} + +static unsigned int asc_get_mctrl(struct uart_port *port) +{ + /* + * This routine is used for geting signals of: DTR, DCD, DSR, RI, + * and CTS/RTS + */ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +/* There are probably characters waiting to be transmitted. */ +static void asc_start_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + if (!uart_circ_empty(xmit)) + asc_enable_tx_interrupts(port); +} + +/* Transmit stop */ +static void asc_stop_tx(struct uart_port *port) +{ + asc_disable_tx_interrupts(port); +} + +/* Receive stop */ +static void asc_stop_rx(struct uart_port *port) +{ + asc_disable_rx_interrupts(port); +} + +/* Force modem status interrupts on */ +static void asc_enable_ms(struct uart_port *port) +{ + /* Nothing here yet .. */ +} + +/* Handle breaks - ignored by us */ +static void asc_break_ctl(struct uart_port *port, int break_state) +{ + /* Nothing here yet .. */ +} + +/* + * Enable port for reception. + */ +static int asc_startup(struct uart_port *port) +{ + if (request_irq(port->irq, asc_interrupt, IRQF_NO_SUSPEND, + asc_port_name(port), port)) { + dev_err(port->dev, "cannot allocate irq.\n"); + return -ENODEV; + } + + asc_transmit_chars(port); + asc_enable_rx_interrupts(port); + + return 0; +} + +static void asc_shutdown(struct uart_port *port) +{ + asc_disable_tx_interrupts(port); + asc_disable_rx_interrupts(port); + free_irq(port->irq, port); +} + +static void asc_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct asc_port *ascport = to_asc_port(port); + unsigned long flags = 0; + u32 ctl; + + switch (state) { + case UART_PM_STATE_ON: + clk_prepare_enable(ascport->clk); + break; + case UART_PM_STATE_OFF: + /* + * Disable the ASC baud rate generator, which is as close as + * we can come to turning it off. Note this is not called with + * the port spinlock held. + */ + spin_lock_irqsave(&port->lock, flags); + ctl = asc_in(port, ASC_CTL) & ~ASC_CTL_RUN; + asc_out(port, ASC_CTL, ctl); + spin_unlock_irqrestore(&port->lock, flags); + clk_disable_unprepare(ascport->clk); + break; + } +} + +static void asc_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct asc_port *ascport = to_asc_port(port); + unsigned int baud; + u32 ctrl_val; + tcflag_t cflag; + unsigned long flags; + + /* Update termios to reflect hardware capabilities */ + termios->c_cflag &= ~(CMSPAR | + (ascport->hw_flow_control ? 0 : CRTSCTS)); + + port->uartclk = clk_get_rate(ascport->clk); + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + cflag = termios->c_cflag; + + spin_lock_irqsave(&port->lock, flags); + + /* read control register */ + ctrl_val = asc_in(port, ASC_CTL); + + /* stop serial port and reset value */ + asc_out(port, ASC_CTL, (ctrl_val & ~ASC_CTL_RUN)); + ctrl_val = ASC_CTL_RXENABLE | ASC_CTL_FIFOENABLE; + + /* reset fifo rx & tx */ + asc_out(port, ASC_TXRESET, 1); + asc_out(port, ASC_RXRESET, 1); + + /* set character length */ + if ((cflag & CSIZE) == CS7) { + ctrl_val |= ASC_CTL_MODE_7BIT_PAR; + } else { + ctrl_val |= (cflag & PARENB) ? ASC_CTL_MODE_8BIT_PAR : + ASC_CTL_MODE_8BIT; + } + + /* set stop bit */ + ctrl_val |= (cflag & CSTOPB) ? ASC_CTL_STOP_2BIT : ASC_CTL_STOP_1BIT; + + /* odd parity */ + if (cflag & PARODD) + ctrl_val |= ASC_CTL_PARITYODD; + + /* hardware flow control */ + if ((cflag & CRTSCTS)) + ctrl_val |= ASC_CTL_CTSENABLE; + + if ((baud < 19200) && !ascport->force_m1) { + asc_out(port, ASC_BAUDRATE, (port->uartclk / (16 * baud))); + } else { + /* + * MODE 1: recommended for high bit rates (above 19.2K) + * + * baudrate * 16 * 2^16 + * ASCBaudRate = ------------------------ + * inputclock + * + * However to keep the maths inside 32bits we divide top and + * bottom by 64. The +1 is to avoid a divide by zero if the + * input clock rate is something unexpected. + */ + u32 counter = (baud * 16384) / ((port->uartclk / 64) + 1); + asc_out(port, ASC_BAUDRATE, counter); + ctrl_val |= ASC_CTL_BAUDMODE; + } + + uart_update_timeout(port, cflag, baud); + + ascport->port.read_status_mask = ASC_RXBUF_DUMMY_OE; + if (termios->c_iflag & INPCK) + ascport->port.read_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + ascport->port.read_status_mask |= ASC_RXBUF_DUMMY_BE; + + /* + * Characters to ignore + */ + ascport->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + ascport->port.ignore_status_mask |= ASC_RXBUF_FE | ASC_RXBUF_PE; + if (termios->c_iflag & IGNBRK) { + ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_OE; + } + + /* + * Ignore all characters if CREAD is not set. + */ + if (!(termios->c_cflag & CREAD)) + ascport->port.ignore_status_mask |= ASC_RXBUF_DUMMY_RX; + + /* Set the timeout */ + asc_out(port, ASC_TIMEOUT, 20); + + /* write final value and enable port */ + asc_out(port, ASC_CTL, (ctrl_val | ASC_CTL_RUN)); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *asc_type(struct uart_port *port) +{ + return (port->type == PORT_ASC) ? DRIVER_NAME : NULL; +} + +static void asc_release_port(struct uart_port *port) +{ +} + +static int asc_request_port(struct uart_port *port) +{ + return 0; +} + +/* + * Called when the port is opened, and UPF_BOOT_AUTOCONF flag is set + * Set type field if successful + */ +static void asc_config_port(struct uart_port *port, int flags) +{ + if ((flags & UART_CONFIG_TYPE)) + port->type = PORT_ASC; +} + +static int +asc_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* No user changeable parameters */ + return -EINVAL; +} + +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context (i.e. kgdb). + */ + +static int asc_get_poll_char(struct uart_port *port) +{ + if (!(asc_in(port, ASC_STA) & ASC_STA_RBF)) + return NO_POLL_CHAR; + + return asc_in(port, ASC_RXBUF); +} + +static void asc_put_poll_char(struct uart_port *port, unsigned char c) +{ + while (asc_txfifo_is_full(port)) + cpu_relax(); + asc_out(port, ASC_TXBUF, c); +} + +#endif /* CONFIG_CONSOLE_POLL */ + +/*---------------------------------------------------------------------*/ + +static struct uart_ops asc_uart_ops = { + .tx_empty = asc_tx_empty, + .set_mctrl = asc_set_mctrl, + .get_mctrl = asc_get_mctrl, + .start_tx = asc_start_tx, + .stop_tx = asc_stop_tx, + .stop_rx = asc_stop_rx, + .enable_ms = asc_enable_ms, + .break_ctl = asc_break_ctl, + .startup = asc_startup, + .shutdown = asc_shutdown, + .set_termios = asc_set_termios, + .type = asc_type, + .release_port = asc_release_port, + .request_port = asc_request_port, + .config_port = asc_config_port, + .verify_port = asc_verify_port, + .pm = asc_pm, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = asc_get_poll_char, + .poll_put_char = asc_put_poll_char, +#endif /* CONFIG_CONSOLE_POLL */ +}; + +static int asc_init_port(struct asc_port *ascport, + struct platform_device *pdev) +{ + struct uart_port *port = &ascport->port; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) { + dev_err(&pdev->dev, "Unable to get io resource\n"); + return -ENODEV; + } + + port->iotype = UPIO_MEM; + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &asc_uart_ops; + port->fifosize = ASC_FIFO_SIZE; + port->dev = &pdev->dev; + port->mapbase = res->start; + port->irq = platform_get_irq(pdev, 0); + + port->membase = devm_request_and_ioremap(&pdev->dev, res); + if (!port->membase) { + dev_err(&pdev->dev, "Unable to request io memory\n"); + return -ENODEV; + } + + spin_lock_init(&port->lock); + + ascport->clk = devm_clk_get(&pdev->dev, NULL); + + if (WARN_ON(IS_ERR(ascport->clk))) + return -EINVAL; + /* ensure that clk rate is correct by enabling the clk */ + clk_prepare_enable(ascport->clk); + ascport->port.uartclk = clk_get_rate(ascport->clk); + WARN_ON(ascport->port.uartclk == 0); + clk_disable_unprepare(ascport->clk); + + return 0; +} + +static struct asc_port *asc_of_get_asc_port(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int id; + + if (!np) + return NULL; + + id = of_alias_get_id(np, ASC_SERIAL_NAME); + + if (id < 0) + id = 0; + + if (WARN_ON(id >= ASC_MAX_PORTS)) + return NULL; + + asc_ports[id].hw_flow_control = of_property_read_bool(np, + "st,hw-flow-control"); + asc_ports[id].force_m1 = of_property_read_bool(np, "st,force_m1"); + asc_ports[id].port.line = id; + return &asc_ports[id]; +} + +static struct of_device_id asc_match[] = { + { .compatible = "st,asc", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, asc_match); + +static int asc_serial_probe(struct platform_device *pdev) +{ + int ret; + struct asc_port *ascport; + + ascport = asc_of_get_asc_port(pdev); + if (!ascport) + return -ENODEV; + + ret = asc_init_port(ascport, pdev); + if (ret) + return ret; + + ret = uart_add_one_port(&asc_uart_driver, &ascport->port); + if (ret) + return ret; + + platform_set_drvdata(pdev, &ascport->port); + + return 0; +} + +static int asc_serial_remove(struct platform_device *pdev) +{ + struct uart_port *port = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + return uart_remove_one_port(&asc_uart_driver, port); +} + +#ifdef CONFIG_PM_SLEEP +static int asc_serial_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct uart_port *port = platform_get_drvdata(pdev); + + return uart_suspend_port(&asc_uart_driver, port); +} + +static int asc_serial_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct uart_port *port = platform_get_drvdata(pdev); + + return uart_resume_port(&asc_uart_driver, port); +} + +#endif /* CONFIG_PM_SLEEP */ + +/*----------------------------------------------------------------------*/ + +#ifdef CONFIG_SERIAL_ST_ASC_CONSOLE +static void asc_console_putchar(struct uart_port *port, int ch) +{ + unsigned int timeout = 1000000; + + /* Wait for upto 1 second in case flow control is stopping us. */ + while (--timeout && asc_txfifo_is_full(port)) + udelay(1); + + asc_out(port, ASC_TXBUF, ch); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + */ + +static void asc_console_write(struct console *co, const char *s, unsigned count) +{ + struct uart_port *port = &asc_ports[co->index].port; + unsigned long flags; + unsigned long timeout = 1000000; + int locked = 1; + u32 intenable; + + local_irq_save(flags); + if (port->sysrq) + locked = 0; /* asc_interrupt has already claimed the lock */ + else if (oops_in_progress) + locked = spin_trylock(&port->lock); + else + spin_lock(&port->lock); + + /* + * Disable interrupts so we don't get the IRQ line bouncing + * up and down while interrupts are disabled. + */ + intenable = asc_in(port, ASC_INTEN); + asc_out(port, ASC_INTEN, 0); + (void)asc_in(port, ASC_INTEN); /* Defeat bus write posting */ + + uart_console_write(port, s, count, asc_console_putchar); + + while (--timeout && !asc_txfifo_is_empty(port)) + udelay(1); + + asc_out(port, ASC_INTEN, intenable); + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); +} + +static int asc_console_setup(struct console *co, char *options) +{ + struct asc_port *ascport; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index >= ASC_MAX_PORTS) + return -ENODEV; + + ascport = &asc_ports[co->index]; + + /* + * This driver does not support early console initialization + * (use ARM early printk support instead), so we only expect + * this to be called during the uart port registration when the + * driver gets probed and the port should be mapped at that point. + */ + BUG_ON(ascport->port.mapbase == 0 || ascport->port.membase == NULL); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&ascport->port, co, baud, parity, bits, flow); +} + +static struct console asc_console = { + .name = ASC_SERIAL_NAME, + .device = uart_console_device, + .write = asc_console_write, + .setup = asc_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &asc_uart_driver, +}; + +#define ASC_SERIAL_CONSOLE (&asc_console) + +#else +#define ASC_SERIAL_CONSOLE NULL +#endif /* CONFIG_SERIAL_ST_ASC_CONSOLE */ + +static struct uart_driver asc_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = ASC_SERIAL_NAME, + .major = 0, + .minor = 0, + .nr = ASC_MAX_PORTS, + .cons = ASC_SERIAL_CONSOLE, +}; + +static const struct dev_pm_ops asc_serial_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(asc_serial_suspend, asc_serial_resume) +}; + +static struct platform_driver asc_serial_driver = { + .probe = asc_serial_probe, + .remove = asc_serial_remove, + .driver = { + .name = DRIVER_NAME, + .pm = &asc_serial_pm_ops, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(asc_match), + }, +}; + +static int __init asc_init(void) +{ + int ret; + static char banner[] __initdata = + KERN_INFO "STMicroelectronics ASC driver initialized\n"; + + printk(banner); + + ret = uart_register_driver(&asc_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&asc_serial_driver); + if (ret) + uart_unregister_driver(&asc_uart_driver); + + return ret; +} + +static void __exit asc_exit(void) +{ + platform_driver_unregister(&asc_serial_driver); + uart_unregister_driver(&asc_uart_driver); +} + +module_init(asc_init); +module_exit(asc_exit); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("STMicroelectronics (R&D) Limited"); +MODULE_DESCRIPTION("STMicroelectronics ASC serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 9119cc0977bf..e40ebe124ced 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -232,4 +232,7 @@ /* SH-SCI */ #define PORT_HSCIF 104 +/* ST ASC type numbers */ +#define PORT_ASC 105 + #endif /* _UAPILINUX_SERIAL_CORE_H */ -- cgit v1.2.3-71-gd317 From 12197de2cdb2ded83db7b5a3a06f4b275fb8e62c Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Tue, 16 Jul 2013 16:14:38 +0100 Subject: pci_ids.h: move PCI_VENDOR_ID_AMCC here PCI_VENDOR_ID_AMCC is defined locally in "drivers/staging/comedi/comedidev.h" for a few comedi hardware drivers, namely "adl_pci9118", "addi_apci_1500" and "addi_apci_3120" (also "addi_apci_1710" but that is not currently built and will probably be removed soon). Move the define into "include/linux/pci_ids.h" as it is shared by several drivers (albeit all comedi drivers currently). PCI_VENDOR_ID_AMCC happens to have the same value (0x10e8) as PCI_VENDOR_ID_ADDIDATA_OLD. The vendor ID is actually assigned to Applied Micro Circuits Corporation and Addi-Data were using device IDs assigned by AMCC on some of their earlier PCI boards. The PCI_VENDOR_ID_ADDIDATA_OLD define is still being used by the "8250_pci" PCI serial board driver. Signed-off-by: Ian Abbott Acked-by: Bjorn Helgaas Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/comedidev.h | 1 - include/linux/pci_ids.h | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/staging/comedi/comedidev.h b/drivers/staging/comedi/comedidev.h index b75915f30f48..9f4f73fa652c 100644 --- a/drivers/staging/comedi/comedidev.h +++ b/drivers/staging/comedi/comedidev.h @@ -400,7 +400,6 @@ int comedi_driver_unregister(struct comedi_driver *); */ #define PCI_VENDOR_ID_KOLTER 0x1001 #define PCI_VENDOR_ID_ICP 0x104c -#define PCI_VENDOR_ID_AMCC 0x10e8 #define PCI_VENDOR_ID_DT 0x1116 #define PCI_VENDOR_ID_IOTECH 0x1616 #define PCI_VENDOR_ID_CONTEC 0x1221 diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 3bed2e89611b..9d9911926598 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1311,6 +1311,8 @@ #define PCI_DEVICE_ID_IMS_TT128 0x9128 #define PCI_DEVICE_ID_IMS_TT3D 0x9135 +#define PCI_VENDOR_ID_AMCC 0x10e8 + #define PCI_VENDOR_ID_INTERG 0x10ea #define PCI_DEVICE_ID_INTERG_1682 0x1682 #define PCI_DEVICE_ID_INTERG_2000 0x2000 @@ -2256,7 +2258,7 @@ /* * ADDI-DATA GmbH communication cards */ -#define PCI_VENDOR_ID_ADDIDATA_OLD 0x10E8 +#define PCI_VENDOR_ID_ADDIDATA_OLD 0x10E8 /* actually AMCC */ #define PCI_VENDOR_ID_ADDIDATA 0x15B8 #define PCI_DEVICE_ID_ADDIDATA_APCI7500 0x7000 #define PCI_DEVICE_ID_ADDIDATA_APCI7420 0x7001 -- cgit v1.2.3-71-gd317 From fee8bf4b74e37bfeb8998a349cb788680eb3f634 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Tue, 16 Jul 2013 16:14:41 +0100 Subject: pci_ids.h: remove PCI_VENDOR_ID_ADDIDATA_OLD and PCI_DEVICE_ID_ADDIDATA_APCI7800 These two defines are no longer used. They were only used by the PCI serial driver "8250_pci" to support the original ADDI-DATA APCI-7800 card. In that driver, PCI_VENDOR_ID_ADDIDATA_OLD has been replaced with PCI_VENDOR_ID_AMCC which has the same value (0x10e8), and PCI_DEVICE_ID_ADDIDATA_APCI7800 has been replaced with a local #define PCI_DEVICE_ID_AMCC_ADDIDATA_APCI7800 with the same value (0x818e). Signed-off-by: Ian Abbott Acked-by: Bjorn Helgaas Signed-off-by: Greg Kroah-Hartman --- include/linux/pci_ids.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 9d9911926598..6dec3d6abe0b 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2258,12 +2258,10 @@ /* * ADDI-DATA GmbH communication cards */ -#define PCI_VENDOR_ID_ADDIDATA_OLD 0x10E8 /* actually AMCC */ #define PCI_VENDOR_ID_ADDIDATA 0x15B8 #define PCI_DEVICE_ID_ADDIDATA_APCI7500 0x7000 #define PCI_DEVICE_ID_ADDIDATA_APCI7420 0x7001 #define PCI_DEVICE_ID_ADDIDATA_APCI7300 0x7002 -#define PCI_DEVICE_ID_ADDIDATA_APCI7800 0x818E #define PCI_DEVICE_ID_ADDIDATA_APCI7500_2 0x7009 #define PCI_DEVICE_ID_ADDIDATA_APCI7420_2 0x700A #define PCI_DEVICE_ID_ADDIDATA_APCI7300_2 0x700B -- cgit v1.2.3-71-gd317 From cf940ebe98e693aec2d10f9af2fb84eb55234e3c Mon Sep 17 00:00:00 2001 From: Andreas Platschek Date: Fri, 26 Jul 2013 06:46:37 +0200 Subject: tty: Remove dead code -> The ledptrs[] array is never initialized. -> There is no place where kbd->ledmode is set to LED_SHOW_MEM therefore the if statement does not make much sense. -> Since LED_SHOW_MEM is not used, it can be removed from the header file as well. Signed-off-by: Andreas Platschek Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 21 +-------------------- include/linux/kbd_kern.h | 3 +-- 2 files changed, 2 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index a9af1b9ae160..d0e3a4497707 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -132,12 +132,6 @@ static int shift_state = 0; static unsigned char ledstate = 0xff; /* undefined */ static unsigned char ledioctl; -static struct ledptr { - unsigned int *addr; - unsigned int mask; - unsigned char valid:1; -} ledptrs[3]; - /* * Notifier list for console keyboard events */ @@ -994,24 +988,11 @@ void setledstate(struct kbd_struct *kbd, unsigned int led) static inline unsigned char getleds(void) { struct kbd_struct *kbd = kbd_table + fg_console; - unsigned char leds; - int i; if (kbd->ledmode == LED_SHOW_IOCTL) return ledioctl; - leds = kbd->ledflagstate; - - if (kbd->ledmode == LED_SHOW_MEM) { - for (i = 0; i < 3; i++) - if (ledptrs[i].valid) { - if (*ledptrs[i].addr & ledptrs[i].mask) - leds |= (1 << i); - else - leds &= ~(1 << i); - } - } - return leds; + return kbd->ledflagstate; } static int kbd_update_leds_helper(struct input_handle *handle, void *data) diff --git a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h index b7c8cdc1d422..cbfb171bbcba 100644 --- a/include/linux/kbd_kern.h +++ b/include/linux/kbd_kern.h @@ -36,10 +36,9 @@ struct kbd_struct { #define VC_CTRLRLOCK KG_CTRLR /* ctrlr lock mode */ unsigned char slockstate; /* for `sticky' Shift, Ctrl, etc. */ - unsigned char ledmode:2; /* one 2-bit value */ + unsigned char ledmode:1; #define LED_SHOW_FLAGS 0 /* traditional state */ #define LED_SHOW_IOCTL 1 /* only change leds upon ioctl */ -#define LED_SHOW_MEM 2 /* `heartbeat': peek into memory */ unsigned char ledflagstate:4; /* flags, not lights */ unsigned char default_ledflagstate:4; -- cgit v1.2.3-71-gd317 From 6741335bc7294548ac95cb1f4671991ff30da193 Mon Sep 17 00:00:00 2001 From: "K. Y. Srinivasan" Date: Tue, 2 Jul 2013 10:31:30 -0700 Subject: Drivers: hv: util: Fix a bug in version negotiation code for util services The current code picked the highest version advertised by the host. WS2012 R2 has implemented a protocol version for KVP that is not compatible with prior protocol versions of KVP. Fix the bug in the current code by explicitly specifying the protocol version that the guest can support. Signed-off-by: K. Y. Srinivasan Reviewed-by: Haiyang Zhang Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 75 +++++++++++++++++++++++++++++++++-------------- drivers/hv/hv_kvp.c | 24 ++++++++++++++- drivers/hv/hv_snapshot.c | 18 ++++-------- drivers/hv/hv_util.c | 21 +++++++++++-- include/linux/hyperv.h | 10 ++++++- 5 files changed, 109 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 0df75908200e..12ec8c801b25 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -48,30 +48,39 @@ struct vmbus_channel_message_table_entry { * @negop is of type &struct icmsg_negotiate. * Set up and fill in default negotiate response message. * - * The max_fw_version specifies the maximum framework version that - * we can support and max _srv_version specifies the maximum service - * version we can support. A special value MAX_SRV_VER can be - * specified to indicate that we can handle the maximum version - * exposed by the host. + * The fw_version specifies the framework version that + * we can support and srv_version specifies the service + * version we can support. * * Mainly used by Hyper-V drivers. */ -void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, +bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, struct icmsg_negotiate *negop, u8 *buf, - int max_fw_version, int max_srv_version) + int fw_version, int srv_version) { - int icframe_vercnt; - int icmsg_vercnt; + int icframe_major, icframe_minor; + int icmsg_major, icmsg_minor; + int fw_major, fw_minor; + int srv_major, srv_minor; int i; + bool found_match = false; icmsghdrp->icmsgsize = 0x10; + fw_major = (fw_version >> 16); + fw_minor = (fw_version & 0xFFFF); + + srv_major = (srv_version >> 16); + srv_minor = (srv_version & 0xFFFF); negop = (struct icmsg_negotiate *)&buf[ sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; - icframe_vercnt = negop->icframe_vercnt; - icmsg_vercnt = negop->icmsg_vercnt; + icframe_major = negop->icframe_vercnt; + icframe_minor = 0; + + icmsg_major = negop->icmsg_vercnt; + icmsg_minor = 0; /* * Select the framework version number we will @@ -79,26 +88,48 @@ void vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, */ for (i = 0; i < negop->icframe_vercnt; i++) { - if (negop->icversion_data[i].major <= max_fw_version) - icframe_vercnt = negop->icversion_data[i].major; + if ((negop->icversion_data[i].major == fw_major) && + (negop->icversion_data[i].minor == fw_minor)) { + icframe_major = negop->icversion_data[i].major; + icframe_minor = negop->icversion_data[i].minor; + found_match = true; + } } + if (!found_match) + goto fw_error; + + found_match = false; + for (i = negop->icframe_vercnt; (i < negop->icframe_vercnt + negop->icmsg_vercnt); i++) { - if (negop->icversion_data[i].major <= max_srv_version) - icmsg_vercnt = negop->icversion_data[i].major; + if ((negop->icversion_data[i].major == srv_major) && + (negop->icversion_data[i].minor == srv_minor)) { + icmsg_major = negop->icversion_data[i].major; + icmsg_minor = negop->icversion_data[i].minor; + found_match = true; + } } /* - * Respond with the maximum framework and service + * Respond with the framework and service * version numbers we can support. */ - negop->icframe_vercnt = 1; - negop->icmsg_vercnt = 1; - negop->icversion_data[0].major = icframe_vercnt; - negop->icversion_data[0].minor = 0; - negop->icversion_data[1].major = icmsg_vercnt; - negop->icversion_data[1].minor = 0; + +fw_error: + if (!found_match) { + negop->icframe_vercnt = 0; + negop->icmsg_vercnt = 0; + } else { + negop->icframe_vercnt = 1; + negop->icmsg_vercnt = 1; + } + + negop->icversion_data[0].major = icframe_major; + negop->icversion_data[0].minor = icframe_minor; + negop->icversion_data[1].major = icmsg_major; + negop->icversion_data[1].minor = icmsg_minor; + return found_match; } EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index ed50e9e83c61..53127209a404 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -29,6 +29,16 @@ #include +/* + * Pre win8 version numbers used in ws2008 and ws 2008 r2 (win7) + */ +#define WIN7_SRV_MAJOR 3 +#define WIN7_SRV_MINOR 0 +#define WIN7_SRV_MAJOR_MINOR (WIN7_SRV_MAJOR << 16 | WIN7_SRV_MINOR) + +#define WIN8_SRV_MAJOR 4 +#define WIN8_SRV_MINOR 0 +#define WIN8_SRV_MAJOR_MINOR (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR) /* * Global state maintained for transaction that is being processed. @@ -593,8 +603,19 @@ void hv_kvp_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + /* + * We start with win8 version and if the host cannot + * support that we use the previous version. + */ + if (vmbus_prep_negotiate_resp(icmsghdrp, negop, + recv_buffer, UTIL_FW_MAJOR_MINOR, + WIN8_SRV_MAJOR_MINOR)) + goto done; + vmbus_prep_negotiate_resp(icmsghdrp, negop, - recv_buffer, MAX_SRV_VER, MAX_SRV_VER); + recv_buffer, UTIL_FW_MAJOR_MINOR, + WIN7_SRV_MAJOR_MINOR); + } else { kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ sizeof(struct vmbuspipe_hdr) + @@ -626,6 +647,7 @@ void hv_kvp_onchannelcallback(void *context) return; } +done: icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index 8ad5653ce447..e4572f3f2834 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -24,6 +24,10 @@ #include #include +#define VSS_MAJOR 5 +#define VSS_MINOR 0 +#define VSS_MAJOR_MINOR (VSS_MAJOR << 16 | VSS_MINOR) + /* @@ -186,18 +190,8 @@ void hv_vss_onchannelcallback(void *context) if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { vmbus_prep_negotiate_resp(icmsghdrp, negop, - recv_buffer, MAX_SRV_VER, MAX_SRV_VER); - /* - * We currently negotiate the highest number the - * host has presented. If this version is not - * atleast 5.0, reject. - */ - negop = (struct icmsg_negotiate *)&recv_buffer[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - if (negop->icversion_data[1].major < 5) - negop->icframe_vercnt = 0; + recv_buffer, UTIL_FW_MAJOR_MINOR, + VSS_MAJOR_MINOR); } else { vss_msg = (struct hv_vss_msg *)&recv_buffer[ sizeof(struct vmbuspipe_hdr) + diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 2f561c5dfe24..c16164d4a28b 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -28,6 +28,18 @@ #include #include +#define SHUTDOWN_MAJOR 3 +#define SHUTDOWN_MINOR 0 +#define SHUTDOWN_MAJOR_MINOR (SHUTDOWN_MAJOR << 16 | SHUTDOWN_MINOR) + +#define TIMESYNCH_MAJOR 3 +#define TIMESYNCH_MINOR 0 +#define TIMESYNCH_MAJOR_MINOR (TIMESYNCH_MAJOR << 16 | TIMESYNCH_MINOR) + +#define HEARTBEAT_MAJOR 3 +#define HEARTBEAT_MINOR 0 +#define HEARTBEAT_MAJOR_MINOR (HEARTBEAT_MAJOR << 16 | HEARTBEAT_MINOR) + static void shutdown_onchannelcallback(void *context); static struct hv_util_service util_shutdown = { .util_cb = shutdown_onchannelcallback, @@ -87,7 +99,8 @@ static void shutdown_onchannelcallback(void *context) if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { vmbus_prep_negotiate_resp(icmsghdrp, negop, - shut_txf_buf, MAX_SRV_VER, MAX_SRV_VER); + shut_txf_buf, UTIL_FW_MAJOR_MINOR, + SHUTDOWN_MAJOR_MINOR); } else { shutdown_msg = (struct shutdown_msg_data *)&shut_txf_buf[ @@ -213,7 +226,8 @@ static void timesync_onchannelcallback(void *context) if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { vmbus_prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf, - MAX_SRV_VER, MAX_SRV_VER); + UTIL_FW_MAJOR_MINOR, + TIMESYNCH_MAJOR_MINOR); } else { timedatap = (struct ictimesync_data *)&time_txf_buf[ sizeof(struct vmbuspipe_hdr) + @@ -253,7 +267,8 @@ static void heartbeat_onchannelcallback(void *context) if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { vmbus_prep_negotiate_resp(icmsghdrp, NULL, - hbeat_txf_buf, MAX_SRV_VER, MAX_SRV_VER); + hbeat_txf_buf, UTIL_FW_MAJOR_MINOR, + HEARTBEAT_MAJOR_MINOR); } else { heartbeat_msg = (struct heartbeat_msg_data *)&hbeat_txf_buf[ diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index fae8bac907ef..49949079cf47 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -27,6 +27,14 @@ #include +/* + * Framework version for util services. + */ + +#define UTIL_FW_MAJOR 3 +#define UTIL_FW_MINOR 0 +#define UTIL_FW_MAJOR_MINOR (UTIL_FW_MAJOR << 16 | UTIL_FW_MINOR) + /* * Implementation of host controlled snapshot of the guest. @@ -1494,7 +1502,7 @@ struct hyperv_service_callback { }; #define MAX_SRV_VER 0x7ffffff -extern void vmbus_prep_negotiate_resp(struct icmsg_hdr *, +extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *, struct icmsg_negotiate *, u8 *, int, int); -- cgit v1.2.3-71-gd317 From 536446772f1181f3afdf332780b5325d1b6980c6 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Fri, 26 Jul 2013 13:30:20 -0600 Subject: PM / Sleep: new trace event to print device suspend and resume times A new trace event is added to PM events to print the time it takes to suspend and resume a device. It generates trace messages that include device, driver, parent information in addition to the type of PM ops invoked as well as the PM event and error status from the PM ops. Example trace below: bash-2239 [000] .... 290.883035: device_pm_report_time: backlight acpi_video0 parent=0000:00:02.0 state=freeze ops=class nsecs=332 err=0 bash-2239 [000] .... 290.883041: device_pm_report_time: rfkill rf kill3 parent=phy0 state=freeze ops=legacy class nsecs=216 err=0 bash-2239 [001] .... 290.973892: device_pm_report_time: ieee80211 phy0 parent=0000:01:00.0 state=freeze ops=legacy class nsecs=90846477 err=0 bash-2239 [001] .... 293.660129: device_pm_report_time: ieee80211 phy0 parent=0000:01:00.0 state=restore ops=legacy class nsecs=101295162 err=0 bash-2239 [001] .... 293.660147: device_pm_report_time: rfkill rfkill3 parent=phy0 state=restore ops=legacy class nsecs=1804 err=0 bash-2239 [001] .... 293.660157: device_pm_report_time: backlight acpi_video0 parent=0000:00:02.0 state=restore ops=class nsecs=757 err=0 Signed-off-by: Shuah Khan Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 77 +++++++++++++++++++++++++------------------- include/trace/events/power.h | 37 +++++++++++++++++++++ 2 files changed, 80 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 5a9b6569dd74..9f098a82cf04 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "../base.h" #include "power.h" @@ -56,6 +57,30 @@ static pm_message_t pm_transition; static int async_error; +static char *pm_verb(int event) +{ + switch (event) { + case PM_EVENT_SUSPEND: + return "suspend"; + case PM_EVENT_RESUME: + return "resume"; + case PM_EVENT_FREEZE: + return "freeze"; + case PM_EVENT_QUIESCE: + return "quiesce"; + case PM_EVENT_HIBERNATE: + return "hibernate"; + case PM_EVENT_THAW: + return "thaw"; + case PM_EVENT_RESTORE: + return "restore"; + case PM_EVENT_RECOVER: + return "recover"; + default: + return "(unknown PM event)"; + } +} + /** * device_pm_sleep_init - Initialize system suspend-related device fields. * @dev: Device object being initialized. @@ -172,16 +197,21 @@ static ktime_t initcall_debug_start(struct device *dev) } static void initcall_debug_report(struct device *dev, ktime_t calltime, - int error) + int error, pm_message_t state, char *info) { - ktime_t delta, rettime; + ktime_t rettime; + s64 nsecs; + + rettime = ktime_get(); + nsecs = (s64) ktime_to_ns(ktime_sub(rettime, calltime)); if (pm_print_times_enabled) { - rettime = ktime_get(); - delta = ktime_sub(rettime, calltime); pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev), - error, (unsigned long long)ktime_to_ns(delta) >> 10); + error, (unsigned long long)nsecs >> 10); } + + trace_device_pm_report_time(dev, info, nsecs, pm_verb(state.event), + error); } /** @@ -309,30 +339,6 @@ static pm_callback_t pm_noirq_op(const struct dev_pm_ops *ops, pm_message_t stat return NULL; } -static char *pm_verb(int event) -{ - switch (event) { - case PM_EVENT_SUSPEND: - return "suspend"; - case PM_EVENT_RESUME: - return "resume"; - case PM_EVENT_FREEZE: - return "freeze"; - case PM_EVENT_QUIESCE: - return "quiesce"; - case PM_EVENT_HIBERNATE: - return "hibernate"; - case PM_EVENT_THAW: - return "thaw"; - case PM_EVENT_RESTORE: - return "restore"; - case PM_EVENT_RECOVER: - return "recover"; - default: - return "(unknown PM event)"; - } -} - static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info) { dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event), @@ -379,7 +385,7 @@ static int dpm_run_callback(pm_callback_t cb, struct device *dev, error = cb(dev); suspend_report_result(cb, error); - initcall_debug_report(dev, calltime, error); + initcall_debug_report(dev, calltime, error, state, info); return error; } @@ -1027,7 +1033,8 @@ EXPORT_SYMBOL_GPL(dpm_suspend_end); * @cb: Suspend callback to execute. */ static int legacy_suspend(struct device *dev, pm_message_t state, - int (*cb)(struct device *dev, pm_message_t state)) + int (*cb)(struct device *dev, pm_message_t state), + char *info) { int error; ktime_t calltime; @@ -1037,7 +1044,7 @@ static int legacy_suspend(struct device *dev, pm_message_t state, error = cb(dev, state); suspend_report_result(cb, error); - initcall_debug_report(dev, calltime, error); + initcall_debug_report(dev, calltime, error, state, info); return error; } @@ -1097,7 +1104,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) goto Run; } else if (dev->class->suspend) { pm_dev_dbg(dev, state, "legacy class "); - error = legacy_suspend(dev, state, dev->class->suspend); + error = legacy_suspend(dev, state, dev->class->suspend, + "legacy class "); goto End; } } @@ -1108,7 +1116,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) callback = pm_op(dev->bus->pm, state); } else if (dev->bus->suspend) { pm_dev_dbg(dev, state, "legacy bus "); - error = legacy_suspend(dev, state, dev->bus->suspend); + error = legacy_suspend(dev, state, dev->bus->suspend, + "legacy bus "); goto End; } } diff --git a/include/trace/events/power.h b/include/trace/events/power.h index 8e42410bd159..cda100d6762d 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -66,6 +66,43 @@ TRACE_EVENT(machine_suspend, TP_printk("state=%lu", (unsigned long)__entry->state) ); +TRACE_EVENT(device_pm_report_time, + + TP_PROTO(struct device *dev, const char *pm_ops, s64 ops_time, + char *pm_event_str, int error), + + TP_ARGS(dev, pm_ops, ops_time, pm_event_str, error), + + TP_STRUCT__entry( + __string(device, dev_name(dev)) + __string(driver, dev_driver_string(dev)) + __string(parent, dev->parent ? dev_name(dev->parent) : "none") + __string(pm_ops, pm_ops ? pm_ops : "none ") + __string(pm_event_str, pm_event_str) + __field(s64, ops_time) + __field(int, error) + ), + + TP_fast_assign( + const char *tmp = dev->parent ? dev_name(dev->parent) : "none"; + const char *tmp_i = pm_ops ? pm_ops : "none "; + + __assign_str(device, dev_name(dev)); + __assign_str(driver, dev_driver_string(dev)); + __assign_str(parent, tmp); + __assign_str(pm_ops, tmp_i); + __assign_str(pm_event_str, pm_event_str); + __entry->ops_time = ops_time; + __entry->error = error; + ), + + /* ops_str has an extra space at the end */ + TP_printk("%s %s parent=%s state=%s ops=%snsecs=%lld err=%d", + __get_str(driver), __get_str(device), __get_str(parent), + __get_str(pm_event_str), __get_str(pm_ops), + __entry->ops_time, __entry->error) +); + DECLARE_EVENT_CLASS(wakeup_source, TP_PROTO(const char *name, unsigned int state), -- cgit v1.2.3-71-gd317 From e7428e95a06fb516fac1308bd0e176e27c0b9287 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 25 Jul 2013 10:20:23 +0930 Subject: virtio-net: put virtio net header inline with data For small packets we can simplify xmit processing by linearizing buffers with the header: most packets seem to have enough head room we can use for this purpose. Since existing hypervisors require that header is the first s/g element, we need a feature bit for this. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 42 +++++++++++++++++++++++++++++++++-------- include/uapi/linux/virtio_net.h | 4 +++- 2 files changed, 37 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 3d2a90a62649..f21600277583 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -106,6 +106,9 @@ struct virtnet_info { /* Has control virtqueue */ bool has_cvq; + /* Host can handle any s/g split between our header and packet data */ + bool any_header_sg; + /* enable config space updates */ bool config_enable; @@ -669,12 +672,28 @@ static void free_old_xmit_skbs(struct send_queue *sq) static int xmit_skb(struct send_queue *sq, struct sk_buff *skb) { - struct skb_vnet_hdr *hdr = skb_vnet_hdr(skb); + struct skb_vnet_hdr *hdr; const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest; struct virtnet_info *vi = sq->vq->vdev->priv; unsigned num_sg; + unsigned hdr_len; + bool can_push; pr_debug("%s: xmit %p %pM\n", vi->dev->name, skb, dest); + if (vi->mergeable_rx_bufs) + hdr_len = sizeof hdr->mhdr; + else + hdr_len = sizeof hdr->hdr; + + can_push = vi->any_header_sg && + !((unsigned long)skb->data & (__alignof__(*hdr) - 1)) && + !skb_header_cloned(skb) && skb_headroom(skb) >= hdr_len; + /* Even if we can, don't push here yet as this would skew + * csum_start offset below. */ + if (can_push) + hdr = (struct skb_vnet_hdr *)(skb->data - hdr_len); + else + hdr = skb_vnet_hdr(skb); if (skb->ip_summed == CHECKSUM_PARTIAL) { hdr->hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; @@ -703,15 +722,18 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb) hdr->hdr.gso_size = hdr->hdr.hdr_len = 0; } - hdr->mhdr.num_buffers = 0; - - /* Encode metadata header at front. */ if (vi->mergeable_rx_bufs) - sg_set_buf(sq->sg, &hdr->mhdr, sizeof hdr->mhdr); - else - sg_set_buf(sq->sg, &hdr->hdr, sizeof hdr->hdr); + hdr->mhdr.num_buffers = 0; - num_sg = skb_to_sgvec(skb, sq->sg + 1, 0, skb->len) + 1; + if (can_push) { + __skb_push(skb, hdr_len); + num_sg = skb_to_sgvec(skb, sq->sg, 0, skb->len); + /* Pull header back to avoid skew in tx bytes calculations. */ + __skb_pull(skb, hdr_len); + } else { + sg_set_buf(sq->sg, hdr, hdr_len); + num_sg = skb_to_sgvec(skb, sq->sg + 1, 0, skb->len) + 1; + } return virtqueue_add_outbuf(sq->vq, sq->sg, num_sg, skb, GFP_ATOMIC); } @@ -1552,6 +1574,9 @@ static int virtnet_probe(struct virtio_device *vdev) if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) vi->mergeable_rx_bufs = true; + if (virtio_has_feature(vdev, VIRTIO_F_ANY_LAYOUT)) + vi->any_header_sg = true; + if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) vi->has_cvq = true; @@ -1727,6 +1752,7 @@ static unsigned int features[] = { VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN, VIRTIO_NET_F_GUEST_ANNOUNCE, VIRTIO_NET_F_MQ, VIRTIO_NET_F_CTRL_MAC_ADDR, + VIRTIO_F_ANY_LAYOUT, }; static struct virtio_driver virtio_net_driver = { diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h index c520203fac2f..227d4ce84e79 100644 --- a/include/uapi/linux/virtio_net.h +++ b/include/uapi/linux/virtio_net.h @@ -70,7 +70,9 @@ struct virtio_net_config { __u16 max_virtqueue_pairs; } __attribute__((packed)); -/* This is the first element of the scatter-gather list. If you don't +/* This header comes first in the scatter-gather list. + * If VIRTIO_F_ANY_LAYOUT is not negotiated, it must + * be the first element of the scatter-gather list. If you don't * specify GSO or CSUM features, you can simply ignore the header. */ struct virtio_net_hdr { #define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset -- cgit v1.2.3-71-gd317 From 024ec3deac33ddbd81f3c887506f132b24ea21a7 Mon Sep 17 00:00:00 2001 From: Joe Stringer Date: Thu, 25 Jul 2013 10:52:05 +0900 Subject: net/sctp: Refactor SCTP skb checksum computation This patch consolidates the SCTP checksum calculation code from various places to a single new function, sctp_compute_cksum(skb, offset). Signed-off-by: Joe Stringer Reviewed-by: Julian Anastasov Acked-by: Simon Horman Signed-off-by: David S. Miller --- include/net/sctp/checksum.h | 15 +++++++++++++++ net/netfilter/ipvs/ip_vs_proto_sctp.c | 23 +++-------------------- net/netfilter/nf_nat_proto_sctp.c | 8 +------- net/sctp/input.c | 10 +--------- 4 files changed, 20 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/include/net/sctp/checksum.h b/include/net/sctp/checksum.h index 78b88aa8c810..483e6303458d 100644 --- a/include/net/sctp/checksum.h +++ b/include/net/sctp/checksum.h @@ -85,4 +85,19 @@ static inline __le32 sctp_end_cksum(__u32 crc32) return cpu_to_le32(~crc32); } +/* Calculate the CRC32C checksum of an SCTP packet. */ +static inline __le32 sctp_compute_cksum(const struct sk_buff *skb, + unsigned int offset) +{ + const struct sk_buff *iter; + + __u32 crc32 = sctp_start_cksum(skb->data + offset, + skb_headlen(skb) - offset); + skb_walk_frags(skb, iter) + crc32 = sctp_update_cksum((__u8 *) iter->data, + skb_headlen(iter), crc32); + + return sctp_end_cksum(crc32); +} + #endif /* __sctp_checksum_h__ */ diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 3c0da8728036..23e596e438b3 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -66,15 +66,7 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, static void sctp_nat_csum(struct sk_buff *skb, sctp_sctphdr_t *sctph, unsigned int sctphoff) { - __u32 crc32; - struct sk_buff *iter; - - crc32 = sctp_start_cksum((__u8 *)sctph, skb_headlen(skb) - sctphoff); - skb_walk_frags(skb, iter) - crc32 = sctp_update_cksum((u8 *) iter->data, - skb_headlen(iter), crc32); - sctph->checksum = sctp_end_cksum(crc32); - + sctph->checksum = sctp_compute_cksum(skb, sctphoff); skb->ip_summed = CHECKSUM_UNNECESSARY; } @@ -151,10 +143,7 @@ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp) { unsigned int sctphoff; struct sctphdr *sh, _sctph; - struct sk_buff *iter; - __le32 cmp; - __le32 val; - __u32 tmp; + __le32 cmp, val; #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) @@ -168,13 +157,7 @@ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp) return 0; cmp = sh->checksum; - - tmp = sctp_start_cksum((__u8 *) sh, skb_headlen(skb)); - skb_walk_frags(skb, iter) - tmp = sctp_update_cksum((__u8 *) iter->data, - skb_headlen(iter), tmp); - - val = sctp_end_cksum(tmp); + val = sctp_compute_cksum(skb, sctphoff); if (val != cmp) { /* CRC failure, dump it. */ diff --git a/net/netfilter/nf_nat_proto_sctp.c b/net/netfilter/nf_nat_proto_sctp.c index 396e55d46f90..754536f2c674 100644 --- a/net/netfilter/nf_nat_proto_sctp.c +++ b/net/netfilter/nf_nat_proto_sctp.c @@ -34,9 +34,7 @@ sctp_manip_pkt(struct sk_buff *skb, const struct nf_conntrack_tuple *tuple, enum nf_nat_manip_type maniptype) { - struct sk_buff *frag; sctp_sctphdr_t *hdr; - __u32 crc32; if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) return false; @@ -51,11 +49,7 @@ sctp_manip_pkt(struct sk_buff *skb, hdr->dest = tuple->dst.u.sctp.port; } - crc32 = sctp_start_cksum((u8 *)hdr, skb_headlen(skb) - hdroff); - skb_walk_frags(skb, frag) - crc32 = sctp_update_cksum((u8 *)frag->data, skb_headlen(frag), - crc32); - hdr->checksum = sctp_end_cksum(crc32); + hdr->checksum = sctp_compute_cksum(skb, hdroff); return true; } diff --git a/net/sctp/input.c b/net/sctp/input.c index 7993495a4c0f..fa91aff02388 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -87,15 +87,7 @@ static inline int sctp_rcv_checksum(struct net *net, struct sk_buff *skb) { struct sctphdr *sh = sctp_hdr(skb); __le32 cmp = sh->checksum; - struct sk_buff *list; - __le32 val; - __u32 tmp = sctp_start_cksum((__u8 *)sh, skb_headlen(skb)); - - skb_walk_frags(skb, list) - tmp = sctp_update_cksum((__u8 *)list->data, skb_headlen(list), - tmp); - - val = sctp_end_cksum(tmp); + __le32 val = sctp_compute_cksum(skb, 0); if (val != cmp) { /* CRC failure, dump it. */ -- cgit v1.2.3-71-gd317 From a88c32ae15f25fcf0a3c9fadd92f840a1abf0e43 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 25 Jul 2013 13:47:53 +0800 Subject: USBNET: centralize computing of max rx/tx qlen This patch centralizes computing of max rx/tx qlen, because: - RX_QLEN()/TX_QLEN() is called in hot path - computing depends on device's usb speed, now we have ls/fs, hs, ss, so more checks need to be involved - in fact, max rx/tx qlen should not only depend on device USB speed, but also depend on ethernet link speed, so we need to consider that in future. - if SG support is done, max tx qlen may need change too Generally, hard_mtu and rx_urb_size are changed in bind(), reset() and link_reset() callback, and change mtu network operation, this patches introduces the API of usbnet_update_max_qlen(), and calls it in above path. Signed-off-by: Ming Lei Signed-off-by: David S. Miller --- drivers/net/usb/asix_devices.c | 3 +++ drivers/net/usb/ax88179_178a.c | 3 +++ drivers/net/usb/usbnet.c | 45 ++++++++++++++++++++++++++++++++++-------- include/linux/usb/usbnet.h | 3 +++ 4 files changed, 46 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index ad5d1e4384db..b96ad4f15b8a 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -778,6 +778,9 @@ static int ax88178_change_mtu(struct net_device *net, int new_mtu) dev->hard_mtu = net->mtu + net->hard_header_len; ax88178_set_mfb(dev); + /* max qlen depend on hard_mtu and rx_urb_size */ + usbnet_update_max_qlen(dev); + return 0; } diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 1e3c302d94fe..658540433faf 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -688,6 +688,9 @@ static int ax88179_change_mtu(struct net_device *net, int new_mtu) 2, 2, &tmp16); } + /* max qlen depend on hard_mtu and rx_urb_size */ + usbnet_update_max_qlen(dev); + return 0; } diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 06ee82f557d4..d74de074a438 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -59,15 +59,13 @@ * For high speed, each frame comfortably fits almost 36 max size * Ethernet packets (so queues should be bigger). * - * REVISIT qlens should be members of 'struct usbnet'; the goal is to - * let the USB host controller be busy for 5msec or more before an irq - * is required, under load. Jumbograms change the equation. + * The goal is to let the USB host controller be busy for 5msec or + * more before an irq is required, under load. Jumbograms change + * the equation. */ -#define RX_MAX_QUEUE_MEMORY (60 * 1518) -#define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \ - (RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4) -#define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \ - (RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4) +#define MAX_QUEUE_MEMORY (60 * 1518) +#define RX_QLEN(dev) ((dev)->rx_qlen) +#define TX_QLEN(dev) ((dev)->tx_qlen) // reawaken network queue this soon after stopping; else watchdog barks #define TX_TIMEOUT_JIFFIES (5*HZ) @@ -347,6 +345,22 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) } EXPORT_SYMBOL_GPL(usbnet_skb_return); +/* must be called if hard_mtu or rx_urb_size changed */ +void usbnet_update_max_qlen(struct usbnet *dev) +{ + enum usb_device_speed speed = dev->udev->speed; + + switch (speed) { + case USB_SPEED_HIGH: + dev->rx_qlen = MAX_QUEUE_MEMORY / dev->rx_urb_size; + dev->tx_qlen = MAX_QUEUE_MEMORY / dev->hard_mtu; + break; + default: + dev->rx_qlen = dev->tx_qlen = 4; + } +} +EXPORT_SYMBOL_GPL(usbnet_update_max_qlen); + /*------------------------------------------------------------------------- * @@ -375,6 +389,9 @@ int usbnet_change_mtu (struct net_device *net, int new_mtu) usbnet_unlink_rx_urbs(dev); } + /* max qlen depend on hard_mtu and rx_urb_size */ + usbnet_update_max_qlen(dev); + return 0; } EXPORT_SYMBOL_GPL(usbnet_change_mtu); @@ -843,6 +860,9 @@ int usbnet_open (struct net_device *net) goto done; } + /* hard_mtu or rx_urb_size may change in reset() */ + usbnet_update_max_qlen(dev); + // insist peer be connected if (info->check_connect && (retval = info->check_connect (dev)) < 0) { netif_dbg(dev, ifup, dev->net, "can't open; %d\n", retval); @@ -927,6 +947,9 @@ int usbnet_set_settings (struct net_device *net, struct ethtool_cmd *cmd) if (dev->driver_info->link_reset) dev->driver_info->link_reset(dev); + /* hard_mtu or rx_urb_size may change in link_reset() */ + usbnet_update_max_qlen(dev); + return retval; } @@ -1020,6 +1043,9 @@ static void __handle_link_change(struct usbnet *dev) tasklet_schedule(&dev->bh); } + /* hard_mtu or rx_urb_size may change during link change */ + usbnet_update_max_qlen(dev); + clear_bit(EVENT_LINK_CHANGE, &dev->flags); } @@ -1599,6 +1625,9 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) if ((dev->driver_info->flags & FLAG_WWAN) != 0) SET_NETDEV_DEVTYPE(net, &wwan_type); + /* initialize max rx_qlen and tx_qlen */ + usbnet_update_max_qlen(dev); + status = register_netdev (net); if (status) goto out4; diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index f18d64129f99..8fbc008e183e 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -34,6 +34,7 @@ struct usbnet { struct mutex phy_mutex; unsigned char suspend_count; unsigned char pkt_cnt, pkt_err; + unsigned short rx_qlen, tx_qlen; /* i/o info: pipes etc */ unsigned in, out; @@ -253,4 +254,6 @@ extern void usbnet_link_change(struct usbnet *, bool, bool); extern int usbnet_status_start(struct usbnet *dev, gfp_t mem_flags); extern void usbnet_status_stop(struct usbnet *dev); +extern void usbnet_update_max_qlen(struct usbnet *dev); + #endif /* __LINUX_USB_USBNET_H */ -- cgit v1.2.3-71-gd317 From 82a54d0ebbee03a8dcf4e1e4016a53fed4d6c933 Mon Sep 17 00:00:00 2001 From: Asias He Date: Thu, 25 Jul 2013 17:39:34 +0800 Subject: VSOCK: Move af_vsock.h and vsock_addr.h to include/net This is useful for other VSOCK transport implemented outside the net/vmw_vsock/ directory to use these headers. Signed-off-by: Asias He Acked-by: Andy King Signed-off-by: David S. Miller --- include/net/af_vsock.h | 175 +++++++++++++++++++++++++++++++++++++++++ include/net/vsock_addr.h | 30 +++++++ net/vmw_vsock/af_vsock.c | 3 +- net/vmw_vsock/af_vsock.h | 175 ----------------------------------------- net/vmw_vsock/vmci_transport.c | 2 +- net/vmw_vsock/vmci_transport.h | 4 +- net/vmw_vsock/vsock_addr.c | 3 +- net/vmw_vsock/vsock_addr.h | 30 ------- 8 files changed, 210 insertions(+), 212 deletions(-) create mode 100644 include/net/af_vsock.h create mode 100644 include/net/vsock_addr.h delete mode 100644 net/vmw_vsock/af_vsock.h delete mode 100644 net/vmw_vsock/vsock_addr.h (limited to 'include') diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h new file mode 100644 index 000000000000..7d64d3609ec9 --- /dev/null +++ b/include/net/af_vsock.h @@ -0,0 +1,175 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2007-2013 VMware, Inc. All rights reserved. + * + * 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 and no later version. + * + * 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. + */ + +#ifndef __AF_VSOCK_H__ +#define __AF_VSOCK_H__ + +#include +#include +#include + +#include "vsock_addr.h" + +#define LAST_RESERVED_PORT 1023 + +#define vsock_sk(__sk) ((struct vsock_sock *)__sk) +#define sk_vsock(__vsk) (&(__vsk)->sk) + +struct vsock_sock { + /* sk must be the first member. */ + struct sock sk; + struct sockaddr_vm local_addr; + struct sockaddr_vm remote_addr; + /* Links for the global tables of bound and connected sockets. */ + struct list_head bound_table; + struct list_head connected_table; + /* Accessed without the socket lock held. This means it can never be + * modified outsided of socket create or destruct. + */ + bool trusted; + bool cached_peer_allow_dgram; /* Dgram communication allowed to + * cached peer? + */ + u32 cached_peer; /* Context ID of last dgram destination check. */ + const struct cred *owner; + /* Rest are SOCK_STREAM only. */ + long connect_timeout; + /* Listening socket that this came from. */ + struct sock *listener; + /* Used for pending list and accept queue during connection handshake. + * The listening socket is the head for both lists. Sockets created + * for connection requests are placed in the pending list until they + * are connected, at which point they are put in the accept queue list + * so they can be accepted in accept(). If accept() cannot accept the + * connection, it is marked as rejected so the cleanup function knows + * to clean up the socket. + */ + struct list_head pending_links; + struct list_head accept_queue; + bool rejected; + struct delayed_work dwork; + u32 peer_shutdown; + bool sent_request; + bool ignore_connecting_rst; + + /* Private to transport. */ + void *trans; +}; + +s64 vsock_stream_has_data(struct vsock_sock *vsk); +s64 vsock_stream_has_space(struct vsock_sock *vsk); +void vsock_pending_work(struct work_struct *work); +struct sock *__vsock_create(struct net *net, + struct socket *sock, + struct sock *parent, + gfp_t priority, unsigned short type); + +/**** TRANSPORT ****/ + +struct vsock_transport_recv_notify_data { + u64 data1; /* Transport-defined. */ + u64 data2; /* Transport-defined. */ + bool notify_on_block; +}; + +struct vsock_transport_send_notify_data { + u64 data1; /* Transport-defined. */ + u64 data2; /* Transport-defined. */ +}; + +struct vsock_transport { + /* Initialize/tear-down socket. */ + int (*init)(struct vsock_sock *, struct vsock_sock *); + void (*destruct)(struct vsock_sock *); + void (*release)(struct vsock_sock *); + + /* Connections. */ + int (*connect)(struct vsock_sock *); + + /* DGRAM. */ + int (*dgram_bind)(struct vsock_sock *, struct sockaddr_vm *); + int (*dgram_dequeue)(struct kiocb *kiocb, struct vsock_sock *vsk, + struct msghdr *msg, size_t len, int flags); + int (*dgram_enqueue)(struct vsock_sock *, struct sockaddr_vm *, + struct iovec *, size_t len); + bool (*dgram_allow)(u32 cid, u32 port); + + /* STREAM. */ + /* TODO: stream_bind() */ + ssize_t (*stream_dequeue)(struct vsock_sock *, struct iovec *, + size_t len, int flags); + ssize_t (*stream_enqueue)(struct vsock_sock *, struct iovec *, + size_t len); + s64 (*stream_has_data)(struct vsock_sock *); + s64 (*stream_has_space)(struct vsock_sock *); + u64 (*stream_rcvhiwat)(struct vsock_sock *); + bool (*stream_is_active)(struct vsock_sock *); + bool (*stream_allow)(u32 cid, u32 port); + + /* Notification. */ + int (*notify_poll_in)(struct vsock_sock *, size_t, bool *); + int (*notify_poll_out)(struct vsock_sock *, size_t, bool *); + int (*notify_recv_init)(struct vsock_sock *, size_t, + struct vsock_transport_recv_notify_data *); + int (*notify_recv_pre_block)(struct vsock_sock *, size_t, + struct vsock_transport_recv_notify_data *); + int (*notify_recv_pre_dequeue)(struct vsock_sock *, size_t, + struct vsock_transport_recv_notify_data *); + int (*notify_recv_post_dequeue)(struct vsock_sock *, size_t, + ssize_t, bool, struct vsock_transport_recv_notify_data *); + int (*notify_send_init)(struct vsock_sock *, + struct vsock_transport_send_notify_data *); + int (*notify_send_pre_block)(struct vsock_sock *, + struct vsock_transport_send_notify_data *); + int (*notify_send_pre_enqueue)(struct vsock_sock *, + struct vsock_transport_send_notify_data *); + int (*notify_send_post_enqueue)(struct vsock_sock *, ssize_t, + struct vsock_transport_send_notify_data *); + + /* Shutdown. */ + int (*shutdown)(struct vsock_sock *, int); + + /* Buffer sizes. */ + void (*set_buffer_size)(struct vsock_sock *, u64); + void (*set_min_buffer_size)(struct vsock_sock *, u64); + void (*set_max_buffer_size)(struct vsock_sock *, u64); + u64 (*get_buffer_size)(struct vsock_sock *); + u64 (*get_min_buffer_size)(struct vsock_sock *); + u64 (*get_max_buffer_size)(struct vsock_sock *); + + /* Addressing. */ + u32 (*get_local_cid)(void); +}; + +/**** CORE ****/ + +int vsock_core_init(const struct vsock_transport *t); +void vsock_core_exit(void); + +/**** UTILS ****/ + +void vsock_release_pending(struct sock *pending); +void vsock_add_pending(struct sock *listener, struct sock *pending); +void vsock_remove_pending(struct sock *listener, struct sock *pending); +void vsock_enqueue_accept(struct sock *listener, struct sock *connected); +void vsock_insert_connected(struct vsock_sock *vsk); +void vsock_remove_bound(struct vsock_sock *vsk); +void vsock_remove_connected(struct vsock_sock *vsk); +struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr); +struct sock *vsock_find_connected_socket(struct sockaddr_vm *src, + struct sockaddr_vm *dst); +void vsock_for_each_connected_socket(void (*fn)(struct sock *sk)); + +#endif /* __AF_VSOCK_H__ */ diff --git a/include/net/vsock_addr.h b/include/net/vsock_addr.h new file mode 100644 index 000000000000..9ccd5316eac0 --- /dev/null +++ b/include/net/vsock_addr.h @@ -0,0 +1,30 @@ +/* + * VMware vSockets Driver + * + * Copyright (C) 2007-2013 VMware, Inc. All rights reserved. + * + * 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 and no later version. + * + * 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. + */ + +#ifndef _VSOCK_ADDR_H_ +#define _VSOCK_ADDR_H_ + +#include + +void vsock_addr_init(struct sockaddr_vm *addr, u32 cid, u32 port); +int vsock_addr_validate(const struct sockaddr_vm *addr); +bool vsock_addr_bound(const struct sockaddr_vm *addr); +void vsock_addr_unbind(struct sockaddr_vm *addr); +bool vsock_addr_equals_addr(const struct sockaddr_vm *addr, + const struct sockaddr_vm *other); +int vsock_addr_cast(const struct sockaddr *addr, size_t len, + struct sockaddr_vm **out_addr); + +#endif diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 593071dabd1c..57896eee1c25 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -96,8 +96,7 @@ #include #include #include - -#include "af_vsock.h" +#include static int __vsock_bind(struct sock *sk, struct sockaddr_vm *addr); static void vsock_sk_destruct(struct sock *sk); diff --git a/net/vmw_vsock/af_vsock.h b/net/vmw_vsock/af_vsock.h deleted file mode 100644 index 7d64d3609ec9..000000000000 --- a/net/vmw_vsock/af_vsock.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * VMware vSockets Driver - * - * Copyright (C) 2007-2013 VMware, Inc. All rights reserved. - * - * 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 and no later version. - * - * 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. - */ - -#ifndef __AF_VSOCK_H__ -#define __AF_VSOCK_H__ - -#include -#include -#include - -#include "vsock_addr.h" - -#define LAST_RESERVED_PORT 1023 - -#define vsock_sk(__sk) ((struct vsock_sock *)__sk) -#define sk_vsock(__vsk) (&(__vsk)->sk) - -struct vsock_sock { - /* sk must be the first member. */ - struct sock sk; - struct sockaddr_vm local_addr; - struct sockaddr_vm remote_addr; - /* Links for the global tables of bound and connected sockets. */ - struct list_head bound_table; - struct list_head connected_table; - /* Accessed without the socket lock held. This means it can never be - * modified outsided of socket create or destruct. - */ - bool trusted; - bool cached_peer_allow_dgram; /* Dgram communication allowed to - * cached peer? - */ - u32 cached_peer; /* Context ID of last dgram destination check. */ - const struct cred *owner; - /* Rest are SOCK_STREAM only. */ - long connect_timeout; - /* Listening socket that this came from. */ - struct sock *listener; - /* Used for pending list and accept queue during connection handshake. - * The listening socket is the head for both lists. Sockets created - * for connection requests are placed in the pending list until they - * are connected, at which point they are put in the accept queue list - * so they can be accepted in accept(). If accept() cannot accept the - * connection, it is marked as rejected so the cleanup function knows - * to clean up the socket. - */ - struct list_head pending_links; - struct list_head accept_queue; - bool rejected; - struct delayed_work dwork; - u32 peer_shutdown; - bool sent_request; - bool ignore_connecting_rst; - - /* Private to transport. */ - void *trans; -}; - -s64 vsock_stream_has_data(struct vsock_sock *vsk); -s64 vsock_stream_has_space(struct vsock_sock *vsk); -void vsock_pending_work(struct work_struct *work); -struct sock *__vsock_create(struct net *net, - struct socket *sock, - struct sock *parent, - gfp_t priority, unsigned short type); - -/**** TRANSPORT ****/ - -struct vsock_transport_recv_notify_data { - u64 data1; /* Transport-defined. */ - u64 data2; /* Transport-defined. */ - bool notify_on_block; -}; - -struct vsock_transport_send_notify_data { - u64 data1; /* Transport-defined. */ - u64 data2; /* Transport-defined. */ -}; - -struct vsock_transport { - /* Initialize/tear-down socket. */ - int (*init)(struct vsock_sock *, struct vsock_sock *); - void (*destruct)(struct vsock_sock *); - void (*release)(struct vsock_sock *); - - /* Connections. */ - int (*connect)(struct vsock_sock *); - - /* DGRAM. */ - int (*dgram_bind)(struct vsock_sock *, struct sockaddr_vm *); - int (*dgram_dequeue)(struct kiocb *kiocb, struct vsock_sock *vsk, - struct msghdr *msg, size_t len, int flags); - int (*dgram_enqueue)(struct vsock_sock *, struct sockaddr_vm *, - struct iovec *, size_t len); - bool (*dgram_allow)(u32 cid, u32 port); - - /* STREAM. */ - /* TODO: stream_bind() */ - ssize_t (*stream_dequeue)(struct vsock_sock *, struct iovec *, - size_t len, int flags); - ssize_t (*stream_enqueue)(struct vsock_sock *, struct iovec *, - size_t len); - s64 (*stream_has_data)(struct vsock_sock *); - s64 (*stream_has_space)(struct vsock_sock *); - u64 (*stream_rcvhiwat)(struct vsock_sock *); - bool (*stream_is_active)(struct vsock_sock *); - bool (*stream_allow)(u32 cid, u32 port); - - /* Notification. */ - int (*notify_poll_in)(struct vsock_sock *, size_t, bool *); - int (*notify_poll_out)(struct vsock_sock *, size_t, bool *); - int (*notify_recv_init)(struct vsock_sock *, size_t, - struct vsock_transport_recv_notify_data *); - int (*notify_recv_pre_block)(struct vsock_sock *, size_t, - struct vsock_transport_recv_notify_data *); - int (*notify_recv_pre_dequeue)(struct vsock_sock *, size_t, - struct vsock_transport_recv_notify_data *); - int (*notify_recv_post_dequeue)(struct vsock_sock *, size_t, - ssize_t, bool, struct vsock_transport_recv_notify_data *); - int (*notify_send_init)(struct vsock_sock *, - struct vsock_transport_send_notify_data *); - int (*notify_send_pre_block)(struct vsock_sock *, - struct vsock_transport_send_notify_data *); - int (*notify_send_pre_enqueue)(struct vsock_sock *, - struct vsock_transport_send_notify_data *); - int (*notify_send_post_enqueue)(struct vsock_sock *, ssize_t, - struct vsock_transport_send_notify_data *); - - /* Shutdown. */ - int (*shutdown)(struct vsock_sock *, int); - - /* Buffer sizes. */ - void (*set_buffer_size)(struct vsock_sock *, u64); - void (*set_min_buffer_size)(struct vsock_sock *, u64); - void (*set_max_buffer_size)(struct vsock_sock *, u64); - u64 (*get_buffer_size)(struct vsock_sock *); - u64 (*get_min_buffer_size)(struct vsock_sock *); - u64 (*get_max_buffer_size)(struct vsock_sock *); - - /* Addressing. */ - u32 (*get_local_cid)(void); -}; - -/**** CORE ****/ - -int vsock_core_init(const struct vsock_transport *t); -void vsock_core_exit(void); - -/**** UTILS ****/ - -void vsock_release_pending(struct sock *pending); -void vsock_add_pending(struct sock *listener, struct sock *pending); -void vsock_remove_pending(struct sock *listener, struct sock *pending); -void vsock_enqueue_accept(struct sock *listener, struct sock *connected); -void vsock_insert_connected(struct vsock_sock *vsk); -void vsock_remove_bound(struct vsock_sock *vsk); -void vsock_remove_connected(struct vsock_sock *vsk); -struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr); -struct sock *vsock_find_connected_socket(struct sockaddr_vm *src, - struct sockaddr_vm *dst); -void vsock_for_each_connected_socket(void (*fn)(struct sock *sk)); - -#endif /* __AF_VSOCK_H__ */ diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index ffc11df02af2..9d6986634e0b 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -34,8 +34,8 @@ #include #include #include +#include -#include "af_vsock.h" #include "vmci_transport_notify.h" static int vmci_transport_recv_dgram_cb(void *data, struct vmci_datagram *dg); diff --git a/net/vmw_vsock/vmci_transport.h b/net/vmw_vsock/vmci_transport.h index fd88ea8924e4..ce6c9623d5f0 100644 --- a/net/vmw_vsock/vmci_transport.h +++ b/net/vmw_vsock/vmci_transport.h @@ -19,8 +19,8 @@ #include #include -#include "vsock_addr.h" -#include "af_vsock.h" +#include +#include /* If the packet format changes in a release then this should change too. */ #define VMCI_TRANSPORT_PACKET_VERSION 1 diff --git a/net/vmw_vsock/vsock_addr.c b/net/vmw_vsock/vsock_addr.c index ec2611b4ea0e..82486ee55eac 100644 --- a/net/vmw_vsock/vsock_addr.c +++ b/net/vmw_vsock/vsock_addr.c @@ -17,8 +17,7 @@ #include #include #include - -#include "vsock_addr.h" +#include void vsock_addr_init(struct sockaddr_vm *addr, u32 cid, u32 port) { diff --git a/net/vmw_vsock/vsock_addr.h b/net/vmw_vsock/vsock_addr.h deleted file mode 100644 index 9ccd5316eac0..000000000000 --- a/net/vmw_vsock/vsock_addr.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * VMware vSockets Driver - * - * Copyright (C) 2007-2013 VMware, Inc. All rights reserved. - * - * 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 and no later version. - * - * 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. - */ - -#ifndef _VSOCK_ADDR_H_ -#define _VSOCK_ADDR_H_ - -#include - -void vsock_addr_init(struct sockaddr_vm *addr, u32 cid, u32 port); -int vsock_addr_validate(const struct sockaddr_vm *addr); -bool vsock_addr_bound(const struct sockaddr_vm *addr); -void vsock_addr_unbind(struct sockaddr_vm *addr); -bool vsock_addr_equals_addr(const struct sockaddr_vm *addr, - const struct sockaddr_vm *other); -int vsock_addr_cast(const struct sockaddr *addr, size_t len, - struct sockaddr_vm **out_addr); - -#endif -- cgit v1.2.3-71-gd317 From c26bf4a51308c85a6f97628253b99767a84ff90a Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Thu, 25 Jul 2013 18:12:18 +0200 Subject: pktgen: Add UDPCSUM flag to support UDP checksums UDP checksums are optional, hence pktgen has been omitting them in favour of performance. The optional flag UDPCSUM enables UDP checksumming. If the output device supports hardware checksumming the skb is prepared and marked CHECKSUM_PARTIAL, otherwise the checksum is generated in software. Signed-off-by: Thomas Graf Cc: Eric Dumazet Cc: Ben Greear Signed-off-by: David S. Miller --- include/net/udp.h | 1 + net/core/pktgen.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++----- net/ipv4/udp.c | 3 ++- 3 files changed, 55 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/udp.h b/include/net/udp.h index 74c10ec5e74f..ef2e0b7843a0 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -183,6 +183,7 @@ extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len); extern int udp_push_pending_frames(struct sock *sk); extern void udp_flush_pending_frames(struct sock *sk); +extern void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst); extern int udp_rcv(struct sk_buff *skb); extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); extern int udp_disconnect(struct sock *sk, int flags); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 9640972ec50e..48cebf2c3e7a 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -160,6 +160,7 @@ #include #include #include +#include #include #ifdef CONFIG_XFRM #include @@ -198,6 +199,7 @@ #define F_QUEUE_MAP_RND (1<<13) /* queue map Random */ #define F_QUEUE_MAP_CPU (1<<14) /* queue map mirrors smp_processor_id() */ #define F_NODE (1<<15) /* Node memory alloc*/ +#define F_UDPCSUM (1<<16) /* Include UDP checksum */ /* Thread control flag bits */ #define T_STOP (1<<0) /* Stop run */ @@ -631,6 +633,9 @@ static int pktgen_if_show(struct seq_file *seq, void *v) if (pkt_dev->flags & F_UDPDST_RND) seq_printf(seq, "UDPDST_RND "); + if (pkt_dev->flags & F_UDPCSUM) + seq_printf(seq, "UDPCSUM "); + if (pkt_dev->flags & F_MPLS_RND) seq_printf(seq, "MPLS_RND "); @@ -1228,6 +1233,12 @@ static ssize_t pktgen_if_write(struct file *file, else if (strcmp(f, "!NODE_ALLOC") == 0) pkt_dev->flags &= ~F_NODE; + else if (strcmp(f, "UDPCSUM") == 0) + pkt_dev->flags |= F_UDPCSUM; + + else if (strcmp(f, "!UDPCSUM") == 0) + pkt_dev->flags &= ~F_UDPCSUM; + else { sprintf(pg_result, "Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s", @@ -2733,7 +2744,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, udph->source = htons(pkt_dev->cur_udp_src); udph->dest = htons(pkt_dev->cur_udp_dst); udph->len = htons(datalen + 8); /* DATA + udphdr */ - udph->check = 0; /* No checksum */ + udph->check = 0; iph->ihl = 5; iph->version = 4; @@ -2752,6 +2763,24 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, skb->protocol = protocol; skb->dev = odev; skb->pkt_type = PACKET_HOST; + + if (!(pkt_dev->flags & F_UDPCSUM)) { + skb->ip_summed = CHECKSUM_NONE; + } else if (odev->features & NETIF_F_V4_CSUM) { + skb->ip_summed = CHECKSUM_PARTIAL; + skb->csum = 0; + udp4_hwcsum(skb, udph->source, udph->dest); + } else { + __wsum csum = udp_csum(skb); + + /* add protocol-dependent pseudo-header */ + udph->check = csum_tcpudp_magic(udph->source, udph->dest, + datalen + 8, IPPROTO_UDP, csum); + + if (udph->check == 0) + udph->check = CSUM_MANGLED_0; + } + pktgen_finalize_skb(pkt_dev, skb, datalen); #ifdef CONFIG_XFRM @@ -2768,7 +2797,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, struct sk_buff *skb = NULL; __u8 *eth; struct udphdr *udph; - int datalen; + int datalen, udplen; struct ipv6hdr *iph; __be16 protocol = htons(ETH_P_IPV6); __be32 *mpls; @@ -2844,10 +2873,11 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, net_info_ratelimited("increased datalen to %d\n", datalen); } + udplen = datalen + sizeof(struct udphdr); udph->source = htons(pkt_dev->cur_udp_src); udph->dest = htons(pkt_dev->cur_udp_dst); - udph->len = htons(datalen + sizeof(struct udphdr)); - udph->check = 0; /* No checksum */ + udph->len = htons(udplen); + udph->check = 0; *(__be32 *) iph = htonl(0x60000000); /* Version + flow */ @@ -2858,7 +2888,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, iph->hop_limit = 32; - iph->payload_len = htons(sizeof(struct udphdr) + datalen); + iph->payload_len = htons(udplen); iph->nexthdr = IPPROTO_UDP; iph->daddr = pkt_dev->cur_in6_daddr; @@ -2868,6 +2898,23 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, skb->dev = odev; skb->pkt_type = PACKET_HOST; + if (!(pkt_dev->flags & F_UDPCSUM)) { + skb->ip_summed = CHECKSUM_NONE; + } else if (odev->features & NETIF_F_V6_CSUM) { + skb->ip_summed = CHECKSUM_PARTIAL; + skb->csum_start = skb_transport_header(skb) - skb->head; + skb->csum_offset = offsetof(struct udphdr, check); + udph->check = ~csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, 0); + } else { + __wsum csum = udp_csum(skb); + + /* add protocol-dependent pseudo-header */ + udph->check = csum_ipv6_magic(&iph->saddr, &iph->daddr, udplen, IPPROTO_UDP, csum); + + if (udph->check == 0) + udph->check = CSUM_MANGLED_0; + } + pktgen_finalize_skb(pkt_dev, skb, datalen); return skb; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 766e6bab9113..9e88af0e8ab0 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -704,7 +704,7 @@ EXPORT_SYMBOL(udp_flush_pending_frames); * @src: source IP address * @dst: destination IP address */ -static void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst) +void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst) { struct udphdr *uh = udp_hdr(skb); struct sk_buff *frags = skb_shinfo(skb)->frag_list; @@ -740,6 +740,7 @@ static void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst) uh->check = CSUM_MANGLED_0; } } +EXPORT_SYMBOL_GPL(udp4_hwcsum); static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4) { -- cgit v1.2.3-71-gd317 From 1536a968892aa9095aada4b6d2ed326432cd71c8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:35:52 -0700 Subject: ASoC: add Renesas R-Car core feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuits are different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2). (Actually, there are many difference in Generation1 chips) Basically, for the future, Renesas R-Car series will use Gen2 style sound circuit, but driver should care Gen1 also. The main differences between Gen1 and Gen2 peripheral are 1) register offset, 2) data path. This patch adds basic (core) feature for R-Car series sound driver as prototype Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 33 +++ sound/soc/sh/Kconfig | 7 + sound/soc/sh/Makefile | 3 + sound/soc/sh/rcar/Makefile | 2 + sound/soc/sh/rcar/core.c | 554 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 94 ++++++++ 6 files changed, 693 insertions(+) create mode 100644 include/sound/rcar_snd.h create mode 100644 sound/soc/sh/rcar/Makefile create mode 100644 sound/soc/sh/rcar/core.c create mode 100644 sound/soc/sh/rcar/rsnd.h (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h new file mode 100644 index 000000000000..7272b2ea7108 --- /dev/null +++ b/include/sound/rcar_snd.h @@ -0,0 +1,33 @@ +/* + * Renesas R-Car SRU/SCU/SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef RCAR_SND_H +#define RCAR_SND_H + +#include + + +#define RSND_BASE_MAX 0 + +struct rsnd_dai_platform_info { + int ssi_id_playback; + int ssi_id_capture; +}; + +struct rcar_snd_info { + u32 flags; + struct rsnd_dai_platform_info *dai_info; + int dai_info_nr; + int (*start)(int id); + int (*stop)(int id); +}; + +#endif diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 6bcb1164d599..56d8ff6a402d 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -34,6 +34,13 @@ config SND_SOC_SH4_SIU select SH_DMAE select FW_LOADER +config SND_SOC_RCAR + tristate "R-Car series SRU/SCU/SSIU/SSI support" + select SND_SIMPLE_CARD + select RCAR_CLK_ADG + help + This option enables R-Car SUR/SCU/SSIU/SSI sound support + ## ## Boards ## diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile index 849b387d17d9..aaf3dcd1ee2a 100644 --- a/sound/soc/sh/Makefile +++ b/sound/soc/sh/Makefile @@ -12,6 +12,9 @@ obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o +## audio units for R-Car +obj-$(CONFIG_SND_SOC_RCAR) += rcar/ + ## boards snd-soc-sh7760-ac97-objs := sh7760-ac97.o snd-soc-migor-objs := migor.o diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile new file mode 100644 index 000000000000..cd8089f20c94 --- /dev/null +++ b/sound/soc/sh/rcar/Makefile @@ -0,0 +1,2 @@ +snd-soc-rcar-objs := core.o +obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c new file mode 100644 index 000000000000..13b5d50efd06 --- /dev/null +++ b/sound/soc/sh/rcar/core.c @@ -0,0 +1,554 @@ +/* + * Renesas R-Car SRU/SCU/SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * Based on fsi.c + * Kuninori Morimoto + * + * 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. + */ + +/* + * Renesas R-Car sound device structure + * + * Gen1 + * + * SRU : Sound Routing Unit + * - SRC : Sampling Rate Converter + * - CMD + * - CTU : Channel Count Conversion Unit + * - MIX : Mixer + * - DVC : Digital Volume and Mute Function + * - SSI : Serial Sound Interface + * + * Gen2 + * + * SCU : Sampling Rate Converter Unit + * - SRC : Sampling Rate Converter + * - CMD + * - CTU : Channel Count Conversion Unit + * - MIX : Mixer + * - DVC : Digital Volume and Mute Function + * SSIU : Serial Sound Interface Unit + * - SSI : Serial Sound Interface + */ + +/* + * driver data Image + * + * rsnd_priv + * | + * | ** this depends on Gen1/Gen2 + * | + * +- gen + * | + * | ** these depend on data path + * | ** gen and platform data control it + * | + * +- rdai[0] + * | | sru ssiu ssi + * | +- playback -> [mod] -> [mod] -> [mod] -> ... + * | | + * | | sru ssiu ssi + * | +- capture -> [mod] -> [mod] -> [mod] -> ... + * | + * +- rdai[1] + * | | sru ssiu ssi + * | +- playback -> [mod] -> [mod] -> [mod] -> ... + * | | + * | | sru ssiu ssi + * | +- capture -> [mod] -> [mod] -> [mod] -> ... + * ... + * | + * | ** these control ssi + * | + * +- ssi + * | | + * | +- ssi[0] + * | +- ssi[1] + * | +- ssi[2] + * | ... + * | + * | ** these control scu + * | + * +- scu + * | + * +- scu[0] + * +- scu[1] + * +- scu[2] + * ... + * + * + * for_each_rsnd_dai(xx, priv, xx) + * rdai[0] => rdai[1] => rdai[2] => ... + * + * for_each_rsnd_mod(xx, rdai, xx) + * [mod] => [mod] => [mod] => ... + * + * rsnd_dai_call(xxx, fn ) + * [mod]->fn() -> [mod]->fn() -> [mod]->fn()... + * + */ +#include +#include "rsnd.h" + +#define RSND_RATES SNDRV_PCM_RATE_8000_96000 +#define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) + +/* + * rsnd_platform functions + */ +#define rsnd_platform_call(priv, dai, func, param...) \ + (!(priv->info->func) ? -ENODEV : \ + priv->info->func(param)) + + +/* + * rsnd_dai functions + */ +struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id) +{ + return priv->rdai + id; +} + +static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) +{ + struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); + + return rsnd_dai_get(priv, dai->id); +} + +int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io) +{ + return &rdai->playback == io; +} + +/* + * rsnd_soc_dai functions + */ +int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional) +{ + struct snd_pcm_substream *substream = io->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + int pos = io->byte_pos + additional; + + pos %= (runtime->periods * io->byte_per_period); + + return pos; +} + +void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) +{ + io->byte_pos += byte; + + if (io->byte_pos >= io->next_period_byte) { + struct snd_pcm_substream *substream = io->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + + io->period_pos++; + io->next_period_byte += io->byte_per_period; + + if (io->period_pos >= runtime->periods) { + io->byte_pos = 0; + io->period_pos = 0; + io->next_period_byte = io->byte_per_period; + } + + snd_pcm_period_elapsed(substream); + } +} + +static int rsnd_dai_stream_init(struct rsnd_dai_stream *io, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + if (!list_empty(&io->head)) + return -EIO; + + INIT_LIST_HEAD(&io->head); + io->substream = substream; + io->byte_pos = 0; + io->period_pos = 0; + io->byte_per_period = runtime->period_size * + runtime->channels * + samples_to_bytes(runtime, 1); + io->next_period_byte = io->byte_per_period; + + return 0; +} + +static +struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + return rtd->cpu_dai; +} + +static +struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai, + struct snd_pcm_substream *substream) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + return &rdai->playback; + else + return &rdai->capture; +} + +static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); + int ssi_id = rsnd_dai_is_play(rdai, io) ? info->ssi_id_playback : + info->ssi_id_capture; + int ret; + unsigned long flags; + + rsnd_lock(priv, flags); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + ret = rsnd_dai_stream_init(io, substream); + if (ret < 0) + goto dai_trigger_end; + + ret = rsnd_platform_call(priv, dai, start, ssi_id); + if (ret < 0) + goto dai_trigger_end; + + break; + case SNDRV_PCM_TRIGGER_STOP: + ret = rsnd_platform_call(priv, dai, stop, ssi_id); + if (ret < 0) + goto dai_trigger_end; + + break; + default: + ret = -EINVAL; + } + +dai_trigger_end: + rsnd_unlock(priv, flags); + + return ret; +} + +static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + rdai->clk_master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + rdai->clk_master = 0; + break; + default: + return -EINVAL; + } + + /* set clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + rdai->bit_clk_inv = 0; + rdai->frm_clk_inv = 1; + break; + case SND_SOC_DAIFMT_IB_NF: + rdai->bit_clk_inv = 1; + rdai->frm_clk_inv = 0; + break; + case SND_SOC_DAIFMT_IB_IF: + rdai->bit_clk_inv = 1; + rdai->frm_clk_inv = 1; + break; + case SND_SOC_DAIFMT_NB_NF: + default: + rdai->bit_clk_inv = 0; + rdai->frm_clk_inv = 0; + break; + } + + /* set format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + rdai->sys_delay = 0; + rdai->data_alignment = 0; + break; + case SND_SOC_DAIFMT_LEFT_J: + rdai->sys_delay = 1; + rdai->data_alignment = 0; + break; + case SND_SOC_DAIFMT_RIGHT_J: + rdai->sys_delay = 1; + rdai->data_alignment = 1; + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { + .trigger = rsnd_soc_dai_trigger, + .set_fmt = rsnd_soc_dai_set_fmt, +}; + +static int rsnd_dai_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct snd_soc_dai_driver *drv; + struct rsnd_dai *rdai; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dai_platform_info *dai_info; + int dai_nr = info->dai_info_nr; + int i, pid, cid; + + drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL); + rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL); + if (!drv || !rdai) { + dev_err(dev, "dai allocate failed\n"); + return -ENOMEM; + } + + for (i = 0; i < dai_nr; i++) { + dai_info = &info->dai_info[i]; + + pid = dai_info->ssi_id_playback; + cid = dai_info->ssi_id_capture; + + /* + * init rsnd_dai + */ + INIT_LIST_HEAD(&rdai[i].playback.head); + INIT_LIST_HEAD(&rdai[i].capture.head); + + rdai[i].info = dai_info; + + snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i); + + /* + * init snd_soc_dai_driver + */ + drv[i].name = rdai[i].name; + drv[i].ops = &rsnd_soc_dai_ops; + if (pid >= 0) { + drv[i].playback.rates = RSND_RATES; + drv[i].playback.formats = RSND_FMTS; + drv[i].playback.channels_min = 2; + drv[i].playback.channels_max = 2; + } + if (cid >= 0) { + drv[i].capture.rates = RSND_RATES; + drv[i].capture.formats = RSND_FMTS; + drv[i].capture.channels_min = 2; + drv[i].capture.channels_max = 2; + } + + dev_dbg(dev, "%s (%d, %d) probed", rdai[i].name, pid, cid); + } + + priv->dai_nr = dai_nr; + priv->daidrv = drv; + priv->rdai = rdai; + + return 0; +} + +static void rsnd_dai_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} + +/* + * pcm ops + */ +static struct snd_pcm_hardware rsnd_pcm_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE, + .formats = RSND_FMTS, + .rates = RSND_RATES, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 64 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 32, + .fifo_size = 256, +}; + +static int rsnd_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret = 0; + + snd_soc_set_runtime_hwparams(substream, &rsnd_pcm_hardware); + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + + return ret; +} + +static int rsnd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + + return bytes_to_frames(runtime, io->byte_pos); +} + +static struct snd_pcm_ops rsnd_pcm_ops = { + .open = rsnd_pcm_open, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = rsnd_hw_params, + .hw_free = snd_pcm_lib_free_pages, + .pointer = rsnd_pointer, +}; + +/* + * snd_soc_platform + */ + +#define PREALLOC_BUFFER (32 * 1024) +#define PREALLOC_BUFFER_MAX (32 * 1024) + +static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + return snd_pcm_lib_preallocate_pages_for_all( + rtd->pcm, + SNDRV_DMA_TYPE_DEV, + rtd->card->snd_card->dev, + PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); +} + +static void rsnd_pcm_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static struct snd_soc_platform_driver rsnd_soc_platform = { + .ops = &rsnd_pcm_ops, + .pcm_new = rsnd_pcm_new, + .pcm_free = rsnd_pcm_free, +}; + +static const struct snd_soc_component_driver rsnd_soc_component = { + .name = "rsnd", +}; + +/* + * rsnd probe + */ +static int rsnd_probe(struct platform_device *pdev) +{ + struct rcar_snd_info *info; + struct rsnd_priv *priv; + struct device *dev = &pdev->dev; + int ret; + + info = pdev->dev.platform_data; + if (!info) { + dev_err(dev, "driver needs R-Car sound information\n"); + return -ENODEV; + } + + /* + * init priv data + */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(dev, "priv allocate failed\n"); + return -ENODEV; + } + + priv->dev = dev; + priv->info = info; + spin_lock_init(&priv->lock); + + /* + * init each module + */ + ret = rsnd_dai_probe(pdev, info, priv); + if (ret < 0) + return ret; + + /* + * asoc register + */ + ret = snd_soc_register_platform(dev, &rsnd_soc_platform); + if (ret < 0) { + dev_err(dev, "cannot snd soc register\n"); + return ret; + } + + ret = snd_soc_register_component(dev, &rsnd_soc_component, + priv->daidrv, rsnd_dai_nr(priv)); + if (ret < 0) { + dev_err(dev, "cannot snd dai register\n"); + goto exit_snd_soc; + } + + dev_set_drvdata(dev, priv); + + pm_runtime_enable(dev); + + dev_info(dev, "probed\n"); + return ret; + +exit_snd_soc: + snd_soc_unregister_platform(dev); + + return ret; +} + +static int rsnd_remove(struct platform_device *pdev) +{ + struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); + + pm_runtime_disable(&pdev->dev); + + /* + * remove each module + */ + rsnd_dai_remove(pdev, priv); + + return 0; +} + +static struct platform_driver rsnd_driver = { + .driver = { + .name = "rcar_sound", + }, + .probe = rsnd_probe, + .remove = rsnd_remove, +}; +module_platform_driver(rsnd_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas R-Car audio driver"); +MODULE_AUTHOR("Kuninori Morimoto "); +MODULE_ALIAS("platform:rcar-pcm-audio"); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h new file mode 100644 index 000000000000..8d04fd0352bd --- /dev/null +++ b/sound/soc/sh/rcar/rsnd.h @@ -0,0 +1,94 @@ +/* + * Renesas R-Car + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef RSND_H +#define RSND_H + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * pseudo register + * + * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different. + * This driver uses pseudo register in order to hide it. + * see gen1/gen2 for detail + */ +struct rsnd_priv; +struct rsnd_dai; +struct rsnd_dai_stream; + +/* + * R-Car sound DAI + */ +#define RSND_DAI_NAME_SIZE 16 +struct rsnd_dai_stream { + struct list_head head; /* head of rsnd_mod list */ + struct snd_pcm_substream *substream; + int byte_pos; + int period_pos; + int byte_per_period; + int next_period_byte; +}; + +struct rsnd_dai { + char name[RSND_DAI_NAME_SIZE]; + struct rsnd_dai_platform_info *info; /* rcar_snd.h */ + struct rsnd_dai_stream playback; + struct rsnd_dai_stream capture; + + int clk_master:1; + int bit_clk_inv:1; + int frm_clk_inv:1; + int sys_delay:1; + int data_alignment:1; +}; + +#define rsnd_dai_nr(priv) ((priv)->dai_nr) +#define for_each_rsnd_dai(rdai, priv, i) \ + for (i = 0, (rdai) = rsnd_dai_get(priv, i); \ + i < rsnd_dai_nr(priv); \ + i++, (rdai) = rsnd_dai_get(priv, i)) + +struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id); +int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); +#define rsnd_dai_get_platform_info(rdai) ((rdai)->info) + +void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); +int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); + +/* + * R-Car sound priv + */ +struct rsnd_priv { + + struct device *dev; + struct rcar_snd_info *info; + spinlock_t lock; + + /* + * below value will be filled on rsnd_dai_probe() + */ + struct snd_soc_dai_driver *daidrv; + struct rsnd_dai *rdai; + int dai_nr; +}; + +#define rsnd_priv_to_dev(priv) ((priv)->dev) +#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) +#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) + +#endif -- cgit v1.2.3-71-gd317 From 3337744ac41bee00b0068ad5f926dd9c27540809 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:36:21 -0700 Subject: ASoC: add Renesas R-Car Generation feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) The main difference between Gen1 and Gen2 are 1) register offset, 2) data path In order to control Gen1/Gen2 by same method, this patch adds gen.c. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 10 +++ sound/soc/sh/rcar/Makefile | 2 +- sound/soc/sh/rcar/core.c | 58 ++++++++++++++++- sound/soc/sh/rcar/gen.c | 154 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 47 ++++++++++++++ 5 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 sound/soc/sh/rcar/gen.c (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 7272b2ea7108..14942a827fe5 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -22,6 +22,16 @@ struct rsnd_dai_platform_info { int ssi_id_capture; }; +/* + * flags + * + * 0x0000000A + * + * A : generation + */ +#define RSND_GEN1 (1 << 0) /* fixme */ +#define RSND_GEN2 (2 << 0) /* fixme */ + struct rcar_snd_info { u32 flags; struct rsnd_dai_platform_info *dai_info; diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index cd8089f20c94..b2d313b1eb94 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o +snd-soc-rcar-objs := core.o gen.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index a47fda2aa600..bb8959f93a7d 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -107,6 +107,50 @@ priv->info->func(param)) +/* + * basic function + */ +u32 rsnd_read(struct rsnd_priv *priv, + struct rsnd_mod *mod, enum rsnd_reg reg) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + + BUG_ON(!base); + + return ioread32(base); +} + +void rsnd_write(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + struct device *dev = rsnd_priv_to_dev(priv); + + BUG_ON(!base); + + dev_dbg(dev, "w %p : %08x\n", base, data); + + iowrite32(data, base); +} + +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 mask, u32 data) +{ + void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); + struct device *dev = rsnd_priv_to_dev(priv); + u32 val; + + BUG_ON(!base); + + val = ioread32(base); + val &= ~mask; + val |= data & mask; + iowrite32(val, base); + + dev_dbg(dev, "s %p : %08x\n", base, val); +} + /* * rsnd_mod functions */ @@ -289,6 +333,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) goto dai_trigger_end; + ret = rsnd_gen_path_init(priv, rdai, io); + if (ret < 0) + goto dai_trigger_end; + ret = rsnd_dai_call(rdai, io, init); if (ret < 0) goto dai_trigger_end; @@ -306,10 +354,13 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) goto dai_trigger_end; - ret = rsnd_platform_call(priv, dai, stop, ssi_id); + ret = rsnd_gen_path_exit(priv, rdai, io); if (ret < 0) goto dai_trigger_end; + ret = rsnd_platform_call(priv, dai, stop, ssi_id); + if (ret < 0) + goto dai_trigger_end; break; default: ret = -EINVAL; @@ -572,6 +623,10 @@ static int rsnd_probe(struct platform_device *pdev) /* * init each module */ + ret = rsnd_gen_probe(pdev, info, priv); + if (ret < 0) + return ret; + ret = rsnd_dai_probe(pdev, info, priv); if (ret < 0) return ret; @@ -615,6 +670,7 @@ static int rsnd_remove(struct platform_device *pdev) * remove each module */ rsnd_dai_remove(pdev, priv); + rsnd_gen_remove(pdev, priv); return 0; } diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c new file mode 100644 index 000000000000..ec67a796eca2 --- /dev/null +++ b/sound/soc/sh/rcar/gen.c @@ -0,0 +1,154 @@ +/* + * Renesas R-Car Gen1 SRU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * 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 "rsnd.h" + +struct rsnd_gen_ops { + int (*path_init)(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); + int (*path_exit)(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +}; + +struct rsnd_gen_reg_map { + int index; /* -1 : not supported */ + u32 offset_id; /* offset of ssi0, ssi1, ssi2... */ + u32 offset_adr; /* offset of SSICR, SSISR, ... */ +}; + +struct rsnd_gen { + void __iomem *base[RSND_BASE_MAX]; + + struct rsnd_gen_reg_map reg_map[RSND_REG_MAX]; + struct rsnd_gen_ops *ops; +}; + +#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) + +#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) +#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2) + +/* + * Gen2 + * will be filled in the future + */ + +/* + * Gen1 + */ +static int rsnd_gen1_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + return 0; +} + +static void rsnd_gen1_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} + +/* + * Gen + */ +int rsnd_gen_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->ops->path_init(priv, rdai, io); +} + +int rsnd_gen_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->ops->path_exit(priv, rdai, io); +} + +void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + struct device *dev = rsnd_priv_to_dev(priv); + int index; + u32 offset_id, offset_adr; + + if (reg >= RSND_REG_MAX) { + dev_err(dev, "rsnd_reg reg error\n"); + return NULL; + } + + index = gen->reg_map[reg].index; + offset_id = gen->reg_map[reg].offset_id; + offset_adr = gen->reg_map[reg].offset_adr; + + if (index < 0) { + dev_err(dev, "unsupported reg access %d\n", reg); + return NULL; + } + + if (offset_id && mod) + offset_id *= rsnd_mod_id(mod); + + /* + * index/offset were set on gen1/gen2 + */ + + return gen->base[index] + offset_id + offset_adr; +} + +int rsnd_gen_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen; + int i; + + gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); + if (!gen) { + dev_err(dev, "GEN allocate failed\n"); + return -ENOMEM; + } + + priv->gen = gen; + + /* + * see + * rsnd_reg_get() + * rsnd_gen_probe() + */ + for (i = 0; i < RSND_REG_MAX; i++) + gen->reg_map[i].index = -1; + + /* + * init each module + */ + if (rsnd_is_gen1(priv)) + return rsnd_gen1_probe(pdev, info, priv); + + dev_err(dev, "unknown generation R-Car sound device\n"); + + return -ENODEV; +} + +void rsnd_gen_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + if (rsnd_is_gen1(priv)) + rsnd_gen1_remove(pdev, priv); +} diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 65d3835cffbc..8cc36416da25 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -27,11 +27,35 @@ * This driver uses pseudo register in order to hide it. * see gen1/gen2 for detail */ +enum rsnd_reg { + RSND_REG_MAX, +}; + struct rsnd_priv; struct rsnd_mod; struct rsnd_dai; struct rsnd_dai_stream; +/* + * R-Car basic functions + */ +#define rsnd_mod_read(m, r) \ + rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) +#define rsnd_mod_write(m, r, d) \ + rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) +#define rsnd_mod_bset(m, r, s, d) \ + rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) + +#define rsnd_priv_read(p, r) rsnd_read(p, NULL, RSND_REG_##r) +#define rsnd_priv_write(p, r, d) rsnd_write(p, NULL, RSND_REG_##r, d) +#define rsnd_priv_bset(p, r, s, d) rsnd_bset(p, NULL, RSND_REG_##r, s, d) + +u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); +void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data); +void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, + u32 mask, u32 data); + /* * R-Car sound mod */ @@ -116,6 +140,24 @@ int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); +/* + * R-Car Gen1/Gen2 + */ +int rsnd_gen_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_gen_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +int rsnd_gen_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +int rsnd_gen_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io); +void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, + struct rsnd_mod *mod, + enum rsnd_reg reg); + /* * R-Car sound priv */ @@ -125,6 +167,11 @@ struct rsnd_priv { struct rcar_snd_info *info; spinlock_t lock; + /* + * below value will be filled on rsnd_gen_probe() + */ + void *gen; + /* * below value will be filled on rsnd_dai_probe() */ -- cgit v1.2.3-71-gd317 From 07539c1de82cdc0ecbe72b413762b2e920407227 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:36:35 -0700 Subject: ASoC: add Renesas R-Car SCU feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) This patch adds SCU feature on this driver. But, it defines SCU style only, does nothing at this point. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 11 +++- sound/soc/sh/rcar/Makefile | 4 +- sound/soc/sh/rcar/core.c | 5 ++ sound/soc/sh/rcar/gen.c | 95 ++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 21 ++++++++ sound/soc/sh/rcar/scu.c | 125 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 258 insertions(+), 3 deletions(-) create mode 100644 sound/soc/sh/rcar/scu.c (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 14942a827fe5..01f2e453dcbf 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -14,8 +14,15 @@ #include +#define RSND_GEN1_SRU 0 -#define RSND_BASE_MAX 0 +#define RSND_GEN2_SRU 0 + +#define RSND_BASE_MAX 1 + +struct rsnd_scu_platform_info { + u32 flags; +}; struct rsnd_dai_platform_info { int ssi_id_playback; @@ -34,6 +41,8 @@ struct rsnd_dai_platform_info { struct rcar_snd_info { u32 flags; + struct rsnd_scu_platform_info *scu_info; + int scu_info_nr; struct rsnd_dai_platform_info *dai_info; int dai_info_nr; int (*start)(int id); diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index b2d313b1eb94..112b2cfd793b 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o gen.o -obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o +snd-soc-rcar-objs := core.o gen.o scu.o +obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index bb8959f93a7d..02d736bb4f54 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -631,6 +631,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = rsnd_scu_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ @@ -669,6 +673,7 @@ static int rsnd_remove(struct platform_device *pdev) /* * remove each module */ + rsnd_scu_remove(pdev, priv); rsnd_dai_remove(pdev, priv); rsnd_gen_remove(pdev, priv); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index ec67a796eca2..2934c0d731c8 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -45,10 +45,105 @@ struct rsnd_gen { /* * Gen1 */ +static int rsnd_gen1_path_init(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); + struct rsnd_mod *mod; + int ret; + int id; + + /* + * Gen1 is created by SRU/SSI, and this SRU is base module of + * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU) + * + * Easy image is.. + * Gen1 SRU = Gen2 SCU + SSIU + etc + * + * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is + * using fixed path. + * + * Then, SSI id = SCU id here + */ + + if (rsnd_dai_is_play(rdai, io)) + id = info->ssi_id_playback; + else + id = info->ssi_id_capture; + + /* SCU */ + mod = rsnd_scu_mod_get(priv, id); + ret = rsnd_dai_connect(rdai, mod, io); + + return ret; +} + +static int rsnd_gen1_path_exit(struct rsnd_priv *priv, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_mod *mod, *n; + int ret = 0; + + /* + * remove all mod from rdai + */ + for_each_rsnd_mod(mod, n, io) + ret |= rsnd_dai_disconnect(mod); + + return ret; +} + +static struct rsnd_gen_ops rsnd_gen1_ops = { + .path_init = rsnd_gen1_path_init, + .path_exit = rsnd_gen1_path_exit, +}; + +#define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \ + do { \ + (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \ + (g)->reg_map[RSND_REG_##i].offset_id = oi; \ + (g)->reg_map[RSND_REG_##i].offset_adr = oa; \ + } while (0) + +static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) +{ + RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); + RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); +} + static int rsnd_gen1_probe(struct platform_device *pdev, struct rcar_snd_info *info, struct rsnd_priv *priv) { + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + struct resource *sru_res; + + /* + * map address + */ + sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); + if (!sru_res) { + dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n"); + return -ENODEV; + } + + gen->ops = &rsnd_gen1_ops; + + gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); + if (!gen->base[RSND_GEN1_SRU]) { + dev_err(dev, "SRU/SSI/ADG ioremap failed\n"); + return -ENODEV; + } + + rsnd_gen1_reg_map_init(gen); + + dev_dbg(dev, "Gen1 device probed\n"); + dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, + gen->base[RSND_GEN1_SRU]); + return 0; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 8cc36416da25..95a391ff0627 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -28,6 +28,10 @@ * see gen1/gen2 for detail */ enum rsnd_reg { + /* SRU/SCU */ + RSND_REG_SSI_MODE0, + RSND_REG_SSI_MODE1, + RSND_REG_MAX, }; @@ -172,6 +176,12 @@ struct rsnd_priv { */ void *gen; + /* + * below value will be filled on rsnd_scu_probe() + */ + void *scu; + int scu_nr; + /* * below value will be filled on rsnd_dai_probe() */ @@ -184,4 +194,15 @@ struct rsnd_priv { #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) +/* + * R-Car SCU + */ +int rsnd_scu_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_scu_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); +#define rsnd_scu_nr(priv) ((priv)->scu_nr) + #endif diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c new file mode 100644 index 000000000000..c12e65f240a1 --- /dev/null +++ b/sound/soc/sh/rcar/scu.c @@ -0,0 +1,125 @@ +/* + * Renesas R-Car SCU support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * 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 "rsnd.h" + +struct rsnd_scu { + struct rsnd_scu_platform_info *info; /* rcar_snd.h */ + struct rsnd_mod mod; +}; + +#define rsnd_mod_to_scu(_mod) \ + container_of((_mod), struct rsnd_scu, mod) + +#define for_each_rsnd_scu(pos, priv, i) \ + for ((i) = 0; \ + ((i) < rsnd_scu_nr(priv)) && \ + ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ + i++) + +static int rsnd_scu_init(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_scu_quit(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_scu_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_scu_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static struct rsnd_mod_ops rsnd_scu_ops = { + .name = "scu", + .init = rsnd_scu_init, + .quit = rsnd_scu_quit, + .start = rsnd_scu_start, + .stop = rsnd_scu_stop, +}; + +struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) +{ + BUG_ON(id < 0 || id >= rsnd_scu_nr(priv)); + + return &((struct rsnd_scu *)(priv->scu) + id)->mod; +} + +int rsnd_scu_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_scu *scu; + int i, nr; + + /* + * init SCU + */ + nr = info->scu_info_nr; + scu = devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL); + if (!scu) { + dev_err(dev, "SCU allocate failed\n"); + return -ENOMEM; + } + + priv->scu_nr = nr; + priv->scu = scu; + + for_each_rsnd_scu(scu, priv, i) { + rsnd_mod_init(priv, &scu->mod, + &rsnd_scu_ops, i); + scu->info = &info->scu_info[i]; + } + + dev_dbg(dev, "scu probed\n"); + + return 0; +} + +void rsnd_scu_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ +} -- cgit v1.2.3-71-gd317 From dfc9403b7c1f566bb099a12c58aee20589e390f1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:36:46 -0700 Subject: ASoC: add Renesas R-Car ADG feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) This patch adds ADG feature which controls sound clock Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 4 +- sound/soc/sh/rcar/Makefile | 2 +- sound/soc/sh/rcar/adg.c | 234 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sh/rcar/core.c | 5 + sound/soc/sh/rcar/gen.c | 20 +++- sound/soc/sh/rcar/rsnd.h | 27 ++++++ 6 files changed, 288 insertions(+), 4 deletions(-) create mode 100644 sound/soc/sh/rcar/adg.c (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 01f2e453dcbf..6babd6f7b537 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -15,10 +15,12 @@ #include #define RSND_GEN1_SRU 0 +#define RSND_GEN1_ADG 1 #define RSND_GEN2_SRU 0 +#define RSND_GEN2_ADG 1 -#define RSND_BASE_MAX 1 +#define RSND_BASE_MAX 2 struct rsnd_scu_platform_info { u32 flags; diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 112b2cfd793b..c11280cffcfe 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o gen.o scu.o +snd-soc-rcar-objs := core.o gen.o scu.o adg.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c new file mode 100644 index 000000000000..d80deb7ccf13 --- /dev/null +++ b/sound/soc/sh/rcar/adg.c @@ -0,0 +1,234 @@ +/* + * Helper routines for R-Car sound ADG. + * + * Copyright (C) 2013 Kuninori Morimoto + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include "rsnd.h" + +#define CLKA 0 +#define CLKB 1 +#define CLKC 2 +#define CLKI 3 +#define CLKMAX 4 + +struct rsnd_adg { + struct clk *clk[CLKMAX]; + + int rate_of_441khz_div_6; + int rate_of_48khz_div_6; +}; + +#define for_each_rsnd_clk(pos, adg, i) \ + for (i = 0, (pos) = adg->clk[i]; \ + i < CLKMAX; \ + i++, (pos) = adg->clk[i]) +#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) + +static enum rsnd_reg rsnd_adg_ssi_reg_get(int id) +{ + enum rsnd_reg reg; + + /* + * SSI 8 is not connected to ADG. + * it works with SSI 7 + */ + if (id == 8) + return RSND_REG_MAX; + + if (0 <= id && id <= 3) + reg = RSND_REG_AUDIO_CLK_SEL0; + else if (4 <= id && id <= 7) + reg = RSND_REG_AUDIO_CLK_SEL1; + else + reg = RSND_REG_AUDIO_CLK_SEL2; + + return reg; +} + +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + enum rsnd_reg reg; + int id; + + /* + * "mod" = "ssi" here. + * we can get "ssi id" from mod + */ + id = rsnd_mod_id(mod); + reg = rsnd_adg_ssi_reg_get(id); + + rsnd_write(priv, mod, reg, 0); + + return 0; +} + +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_adg *adg = rsnd_priv_to_adg(priv); + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + enum rsnd_reg reg; + int id, shift, i; + u32 data; + int sel_table[] = { + [CLKA] = 0x1, + [CLKB] = 0x2, + [CLKC] = 0x3, + [CLKI] = 0x0, + }; + + dev_dbg(dev, "request clock = %d\n", rate); + + /* + * find suitable clock from + * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI. + */ + data = 0; + for_each_rsnd_clk(clk, adg, i) { + if (rate == clk_get_rate(clk)) { + data = sel_table[i]; + goto found_clock; + } + } + + /* + * find 1/6 clock from BRGA/BRGB + */ + if (rate == adg->rate_of_441khz_div_6) { + data = 0x10; + goto found_clock; + } + + if (rate == adg->rate_of_48khz_div_6) { + data = 0x20; + goto found_clock; + } + + return -EIO; + +found_clock: + + /* + * This "mod" = "ssi" here. + * we can get "ssi id" from mod + */ + id = rsnd_mod_id(mod); + reg = rsnd_adg_ssi_reg_get(id); + + dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", id, i, rate); + + /* + * Enable SSIx clock + */ + shift = (id % 4) * 8; + + rsnd_bset(priv, mod, reg, + 0xFF << shift, + data << shift); + + return 0; +} + +static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) +{ + struct clk *clk; + unsigned long rate; + u32 ckr; + int i; + int brg_table[] = { + [CLKA] = 0x0, + [CLKB] = 0x1, + [CLKC] = 0x4, + [CLKI] = 0x2, + }; + + /* + * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC + * have 44.1kHz or 48kHz base clocks for now. + * + * SSI itself can divide parent clock by 1/1 - 1/16 + * So, BRGA outputs 44.1kHz base parent clock 1/32, + * and, BRGB outputs 48.0kHz base parent clock 1/32 here. + * see + * rsnd_adg_ssi_clk_try_start() + */ + ckr = 0; + adg->rate_of_441khz_div_6 = 0; + adg->rate_of_48khz_div_6 = 0; + for_each_rsnd_clk(clk, adg, i) { + rate = clk_get_rate(clk); + + if (0 == rate) /* not used */ + continue; + + /* RBGA */ + if (!adg->rate_of_441khz_div_6 && (0 == rate % 44100)) { + adg->rate_of_441khz_div_6 = rate / 6; + ckr |= brg_table[i] << 20; + } + + /* RBGB */ + if (!adg->rate_of_48khz_div_6 && (0 == rate % 48000)) { + adg->rate_of_48khz_div_6 = rate / 6; + ckr |= brg_table[i] << 16; + } + } + + rsnd_priv_bset(priv, SSICKR, 0x00FF0000, ckr); + rsnd_priv_write(priv, BRRA, 0x00000002); /* 1/6 */ + rsnd_priv_write(priv, BRRB, 0x00000002); /* 1/6 */ +} + +int rsnd_adg_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct rsnd_adg *adg; + struct device *dev = rsnd_priv_to_dev(priv); + struct clk *clk; + int i; + + adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); + if (!adg) { + dev_err(dev, "ADG allocate failed\n"); + return -ENOMEM; + } + + adg->clk[CLKA] = clk_get(NULL, "audio_clk_a"); + adg->clk[CLKB] = clk_get(NULL, "audio_clk_b"); + adg->clk[CLKC] = clk_get(NULL, "audio_clk_c"); + adg->clk[CLKI] = clk_get(NULL, "audio_clk_internal"); + for_each_rsnd_clk(clk, adg, i) { + if (IS_ERR(clk)) { + dev_err(dev, "Audio clock failed\n"); + return -EIO; + } + } + + rsnd_adg_ssi_clk_init(priv, adg); + + priv->adg = adg; + + dev_dbg(dev, "adg probed\n"); + + return 0; +} + +void rsnd_adg_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_adg *adg = priv->adg; + struct clk *clk; + int i; + + for_each_rsnd_clk(clk, adg, i) + clk_put(clk); +} diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 02d736bb4f54..e588d8a8ae40 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -635,6 +635,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = rsnd_adg_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ @@ -673,6 +677,7 @@ static int rsnd_remove(struct platform_device *pdev) /* * remove each module */ + rsnd_adg_remove(pdev, priv); rsnd_scu_remove(pdev, priv); rsnd_dai_remove(pdev, priv); rsnd_gen_remove(pdev, priv); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 2934c0d731c8..ed21a136354f 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -111,6 +111,15 @@ static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) { RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); + + RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00); + RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04); + RSND_GEN1_REG_MAP(gen, ADG, SSICKR, 0x0, 0x08); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL0, 0x0, 0x0c); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL1, 0x0, 0x10); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c); + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20); } static int rsnd_gen1_probe(struct platform_device *pdev, @@ -120,12 +129,15 @@ static int rsnd_gen1_probe(struct platform_device *pdev, struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen = rsnd_priv_to_gen(priv); struct resource *sru_res; + struct resource *adg_res; /* * map address */ sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); - if (!sru_res) { + adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG); + if (!sru_res || + !adg_res) { dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n"); return -ENODEV; } @@ -133,7 +145,9 @@ static int rsnd_gen1_probe(struct platform_device *pdev, gen->ops = &rsnd_gen1_ops; gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); - if (!gen->base[RSND_GEN1_SRU]) { + gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res); + if (!gen->base[RSND_GEN1_SRU] || + !gen->base[RSND_GEN1_ADG]) { dev_err(dev, "SRU/SSI/ADG ioremap failed\n"); return -ENODEV; } @@ -143,6 +157,8 @@ static int rsnd_gen1_probe(struct platform_device *pdev, dev_dbg(dev, "Gen1 device probed\n"); dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, gen->base[RSND_GEN1_SRU]); + dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start, + gen->base[RSND_GEN1_ADG]); return 0; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 95a391ff0627..344fd59cb7fd 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -32,6 +32,17 @@ enum rsnd_reg { RSND_REG_SSI_MODE0, RSND_REG_SSI_MODE1, + /* ADG */ + RSND_REG_BRRA, + RSND_REG_BRRB, + RSND_REG_SSICKR, + RSND_REG_AUDIO_CLK_SEL0, + RSND_REG_AUDIO_CLK_SEL1, + RSND_REG_AUDIO_CLK_SEL2, + RSND_REG_AUDIO_CLK_SEL3, + RSND_REG_AUDIO_CLK_SEL4, + RSND_REG_AUDIO_CLK_SEL5, + RSND_REG_MAX, }; @@ -162,6 +173,17 @@ void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); +/* + * R-Car ADG + */ +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod); +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); +int rsnd_adg_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_adg_remove(struct platform_device *pdev, + struct rsnd_priv *priv); + /* * R-Car sound priv */ @@ -182,6 +204,11 @@ struct rsnd_priv { void *scu; int scu_nr; + /* + * below value will be filled on rsnd_adg_probe() + */ + void *adg; + /* * below value will be filled on rsnd_dai_probe() */ -- cgit v1.2.3-71-gd317 From ae5c322303fff50b93d60e34c6563f1264a5941b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 21 Jul 2013 21:36:57 -0700 Subject: ASoC: add Renesas R-Car SSI feature Renesas R-Car series sound circuit consists of SSI and its peripheral. But this peripheral circuit is different between R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2) (Actually, there are many difference in Generation1 chips) As 1st protype, this patch adds SSI feature on this driver. But, it is PIO sound playback support only at this point. The DMA transfer, and capture feature will be supported in the future Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 23 +- sound/soc/sh/rcar/Makefile | 2 +- sound/soc/sh/rcar/core.c | 5 + sound/soc/sh/rcar/gen.c | 24 +- sound/soc/sh/rcar/rsnd.h | 23 ++ sound/soc/sh/rcar/ssi.c | 588 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 661 insertions(+), 4 deletions(-) create mode 100644 sound/soc/sh/rcar/ssi.c (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 6babd6f7b537..99d8dd029906 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -16,11 +16,30 @@ #define RSND_GEN1_SRU 0 #define RSND_GEN1_ADG 1 +#define RSND_GEN1_SSI 2 #define RSND_GEN2_SRU 0 #define RSND_GEN2_ADG 1 +#define RSND_GEN2_SSIU 2 +#define RSND_GEN2_SSI 3 -#define RSND_BASE_MAX 2 +#define RSND_BASE_MAX 4 + +/* + * flags + * + * 0xA0000000 + * + * A : clock sharing settings + */ +#define RSND_SSI_CLK_PIN_SHARE (1 << 31) +#define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ +#define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ + +struct rsnd_ssi_platform_info { + int pio_irq; + u32 flags; +}; struct rsnd_scu_platform_info { u32 flags; @@ -43,6 +62,8 @@ struct rsnd_dai_platform_info { struct rcar_snd_info { u32 flags; + struct rsnd_ssi_platform_info *ssi_info; + int ssi_info_nr; struct rsnd_scu_platform_info *scu_info; int scu_info_nr; struct rsnd_dai_platform_info *dai_info; diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index c11280cffcfe..0ff492df7929 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,2 @@ -snd-soc-rcar-objs := core.o gen.o scu.o adg.o +snd-soc-rcar-objs := core.o gen.o scu.o adg.o ssi.o obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index e588d8a8ae40..9a5469d3f352 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -639,6 +639,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = rsnd_ssi_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ @@ -677,6 +681,7 @@ static int rsnd_remove(struct platform_device *pdev) /* * remove each module */ + rsnd_ssi_remove(pdev, priv); rsnd_adg_remove(pdev, priv); rsnd_scu_remove(pdev, priv); rsnd_dai_remove(pdev, priv); diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index ed21a136354f..5e4ae0da4352 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -72,6 +72,12 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv, else id = info->ssi_id_capture; + /* SSI */ + mod = rsnd_ssi_mod_get(priv, id); + ret = rsnd_dai_connect(rdai, mod, io); + if (ret < 0) + return ret; + /* SCU */ mod = rsnd_scu_mod_get(priv, id); ret = rsnd_dai_connect(rdai, mod, io); @@ -120,6 +126,12 @@ static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18); RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c); RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20); + + RSND_GEN1_REG_MAP(gen, SSI, SSICR, 0x40, 0x00); + RSND_GEN1_REG_MAP(gen, SSI, SSISR, 0x40, 0x04); + RSND_GEN1_REG_MAP(gen, SSI, SSITDR, 0x40, 0x08); + RSND_GEN1_REG_MAP(gen, SSI, SSIRDR, 0x40, 0x0c); + RSND_GEN1_REG_MAP(gen, SSI, SSIWSR, 0x40, 0x20); } static int rsnd_gen1_probe(struct platform_device *pdev, @@ -130,14 +142,17 @@ static int rsnd_gen1_probe(struct platform_device *pdev, struct rsnd_gen *gen = rsnd_priv_to_gen(priv); struct resource *sru_res; struct resource *adg_res; + struct resource *ssi_res; /* * map address */ sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG); + ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SSI); if (!sru_res || - !adg_res) { + !adg_res || + !ssi_res) { dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n"); return -ENODEV; } @@ -146,8 +161,10 @@ static int rsnd_gen1_probe(struct platform_device *pdev, gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res); + gen->base[RSND_GEN1_SSI] = devm_ioremap_resource(dev, ssi_res); if (!gen->base[RSND_GEN1_SRU] || - !gen->base[RSND_GEN1_ADG]) { + !gen->base[RSND_GEN1_ADG] || + !gen->base[RSND_GEN1_SSI]) { dev_err(dev, "SRU/SSI/ADG ioremap failed\n"); return -ENODEV; } @@ -159,8 +176,11 @@ static int rsnd_gen1_probe(struct platform_device *pdev, gen->base[RSND_GEN1_SRU]); dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start, gen->base[RSND_GEN1_ADG]); + dev_dbg(dev, "SSI : %08x => %p\n", ssi_res->start, + gen->base[RSND_GEN1_SSI]); return 0; + } static void rsnd_gen1_remove(struct platform_device *pdev, diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 344fd59cb7fd..0e7727cc41db 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -43,6 +43,13 @@ enum rsnd_reg { RSND_REG_AUDIO_CLK_SEL4, RSND_REG_AUDIO_CLK_SEL5, + /* SSI */ + RSND_REG_SSICR, + RSND_REG_SSISR, + RSND_REG_SSITDR, + RSND_REG_SSIRDR, + RSND_REG_SSIWSR, + RSND_REG_MAX, }; @@ -151,6 +158,7 @@ int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod, struct rsnd_dai_stream *io); int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); #define rsnd_dai_get_platform_info(rdai) ((rdai)->info) +#define rsnd_io_to_runtime(io) ((io)->substream->runtime) void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); @@ -209,6 +217,11 @@ struct rsnd_priv { */ void *adg; + /* + * below value will be filled on rsnd_ssi_probe() + */ + void *ssiu; + /* * below value will be filled on rsnd_dai_probe() */ @@ -232,4 +245,14 @@ void rsnd_scu_remove(struct platform_device *pdev, struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); #define rsnd_scu_nr(priv) ((priv)->scu_nr) +/* + * R-Car SSI + */ +int rsnd_ssi_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv); +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv); +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); + #endif diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c new file mode 100644 index 000000000000..061ac7e88309 --- /dev/null +++ b/sound/soc/sh/rcar/ssi.c @@ -0,0 +1,588 @@ +/* + * Renesas R-Car SSIU/SSI support + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Kuninori Morimoto + * + * Based on fsi.c + * Kuninori Morimoto + * + * 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 +#include "rsnd.h" +#define RSND_SSI_NAME_SIZE 16 + +/* + * SSICR + */ +#define FORCE (1 << 31) /* Fixed */ +#define UIEN (1 << 27) /* Underflow Interrupt Enable */ +#define OIEN (1 << 26) /* Overflow Interrupt Enable */ +#define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ +#define DIEN (1 << 24) /* Data Interrupt Enable */ + +#define DWL_8 (0 << 19) /* Data Word Length */ +#define DWL_16 (1 << 19) /* Data Word Length */ +#define DWL_18 (2 << 19) /* Data Word Length */ +#define DWL_20 (3 << 19) /* Data Word Length */ +#define DWL_22 (4 << 19) /* Data Word Length */ +#define DWL_24 (5 << 19) /* Data Word Length */ +#define DWL_32 (6 << 19) /* Data Word Length */ + +#define SWL_32 (3 << 16) /* R/W System Word Length */ +#define SCKD (1 << 15) /* Serial Bit Clock Direction */ +#define SWSD (1 << 14) /* Serial WS Direction */ +#define SCKP (1 << 13) /* Serial Bit Clock Polarity */ +#define SWSP (1 << 12) /* Serial WS Polarity */ +#define SDTA (1 << 10) /* Serial Data Alignment */ +#define DEL (1 << 8) /* Serial Data Delay */ +#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */ +#define TRMD (1 << 1) /* Transmit/Receive Mode Select */ +#define EN (1 << 0) /* SSI Module Enable */ + +/* + * SSISR + */ +#define UIRQ (1 << 27) /* Underflow Error Interrupt Status */ +#define OIRQ (1 << 26) /* Overflow Error Interrupt Status */ +#define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ +#define DIRQ (1 << 24) /* Data Interrupt Status Flag */ + +struct rsnd_ssi { + struct clk *clk; + struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ + struct rsnd_ssi *parent; + struct rsnd_mod mod; + + struct rsnd_dai *rdai; + struct rsnd_dai_stream *io; + u32 cr_own; + u32 cr_clk; + u32 cr_etc; + int err; + unsigned int usrcnt; + unsigned int rate; +}; + +struct rsnd_ssiu { + u32 ssi_mode0; + u32 ssi_mode1; + + int ssi_nr; + struct rsnd_ssi *ssi; +}; + +#define for_each_rsnd_ssi(pos, priv, i) \ + for (i = 0; \ + (i < rsnd_ssi_nr(priv)) && \ + ((pos) = ((struct rsnd_ssiu *)((priv)->ssiu))->ssi + i); \ + i++) + +#define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr) +#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) +#define rsnd_ssi_is_pio(ssi) ((ssi)->info->pio_irq > 0) +#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) +#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) +#define rsnd_ssi_mode_flags(p) ((p)->info->flags) +#define rsnd_ssi_to_ssiu(ssi)\ + (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1) + +static void rsnd_ssi_mode_init(struct rsnd_priv *priv, + struct rsnd_ssiu *ssiu) +{ + struct rsnd_ssi *ssi; + u32 flags; + u32 val; + int i; + + /* + * SSI_MODE0 + */ + ssiu->ssi_mode0 = 0; + for_each_rsnd_ssi(ssi, priv, i) + ssiu->ssi_mode0 |= (1 << i); + + /* + * SSI_MODE1 + */ +#define ssi_parent_set(p, sync, adg, ext) \ + do { \ + ssi->parent = ssiu->ssi + p; \ + if (flags & RSND_SSI_CLK_FROM_ADG) \ + val = adg; \ + else \ + val = ext; \ + if (flags & RSND_SSI_SYNC) \ + val |= sync; \ + } while (0) + + ssiu->ssi_mode1 = 0; + for_each_rsnd_ssi(ssi, priv, i) { + flags = rsnd_ssi_mode_flags(ssi); + + if (!(flags & RSND_SSI_CLK_PIN_SHARE)) + continue; + + val = 0; + switch (i) { + case 1: + ssi_parent_set(0, (1 << 4), (0x2 << 0), (0x1 << 0)); + break; + case 2: + ssi_parent_set(0, (1 << 4), (0x2 << 2), (0x1 << 2)); + break; + case 4: + ssi_parent_set(3, (1 << 20), (0x2 << 16), (0x1 << 16)); + break; + case 8: + ssi_parent_set(7, 0, 0, 0); + break; + } + + ssiu->ssi_mode1 |= val; + } +} + +static void rsnd_ssi_mode_set(struct rsnd_ssi *ssi) +{ + struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi); + + rsnd_mod_write(&ssi->mod, SSI_MODE0, ssiu->ssi_mode0); + rsnd_mod_write(&ssi->mod, SSI_MODE1, ssiu->ssi_mode1); +} + +static void rsnd_ssi_status_check(struct rsnd_mod *mod, + u32 bit) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 status; + int i; + + for (i = 0; i < 1024; i++) { + status = rsnd_mod_read(mod, SSISR); + if (status & bit) + return; + + udelay(50); + } + + dev_warn(dev, "status check failed\n"); +} + +static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, + unsigned int rate) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct device *dev = rsnd_priv_to_dev(priv); + int i, j, ret; + int adg_clk_div_table[] = { + 1, 6, /* see adg.c */ + }; + int ssi_clk_mul_table[] = { + 1, 2, 4, 8, 16, 6, 12, + }; + unsigned int main_rate; + + /* + * Find best clock, and try to start ADG + */ + for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) { + for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { + + /* + * this driver is assuming that + * system word is 64fs (= 2 x 32bit) + * see rsnd_ssi_start() + */ + main_rate = rate / adg_clk_div_table[i] + * 32 * 2 * ssi_clk_mul_table[j]; + + ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate); + if (0 == ret) { + ssi->rate = rate; + ssi->cr_clk = FORCE | SWL_32 | + SCKD | SWSD | CKDV(j); + + dev_dbg(dev, "ssi%d outputs %u Hz\n", + rsnd_mod_id(&ssi->mod), rate); + + return 0; + } + } + } + + dev_err(dev, "unsupported clock rate\n"); + return -EIO; +} + +static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi) +{ + ssi->rate = 0; + ssi->cr_clk = 0; + rsnd_adg_ssi_clk_stop(&ssi->mod); +} + +static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 cr; + + if (0 == ssi->usrcnt) { + clk_enable(ssi->clk); + + if (rsnd_rdai_is_clk_master(rdai)) { + struct snd_pcm_runtime *runtime; + + runtime = rsnd_io_to_runtime(io); + + if (rsnd_ssi_clk_from_parent(ssi)) + rsnd_ssi_hw_start(ssi->parent, rdai, io); + else + rsnd_ssi_master_clk_start(ssi, runtime->rate); + } + } + + cr = ssi->cr_own | + ssi->cr_clk | + ssi->cr_etc | + EN; + + rsnd_mod_write(&ssi->mod, SSICR, cr); + + ssi->usrcnt++; + + dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod)); +} + +static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi, + struct rsnd_dai *rdai) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); + struct device *dev = rsnd_priv_to_dev(priv); + u32 cr; + + if (0 == ssi->usrcnt) /* stop might be called without start */ + return; + + ssi->usrcnt--; + + if (0 == ssi->usrcnt) { + /* + * disable all IRQ, + * and, wait all data was sent + */ + cr = ssi->cr_own | + ssi->cr_clk; + + rsnd_mod_write(&ssi->mod, SSICR, cr | EN); + rsnd_ssi_status_check(&ssi->mod, DIRQ); + + /* + * disable SSI, + * and, wait idle state + */ + rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */ + rsnd_ssi_status_check(&ssi->mod, IIRQ); + + if (rsnd_rdai_is_clk_master(rdai)) { + if (rsnd_ssi_clk_from_parent(ssi)) + rsnd_ssi_hw_stop(ssi->parent, rdai); + else + rsnd_ssi_master_clk_stop(ssi); + } + + clk_disable(ssi->clk); + } + + dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod)); +} + +/* + * SSI mod common functions + */ +static int rsnd_ssi_init(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 cr; + + cr = FORCE; + + /* + * always use 32bit system word for easy clock calculation. + * see also rsnd_ssi_master_clk_enable() + */ + cr |= SWL_32; + + /* + * init clock settings for SSICR + */ + switch (runtime->sample_bits) { + case 16: + cr |= DWL_16; + break; + case 32: + cr |= DWL_24; + break; + default: + return -EIO; + } + + if (rdai->bit_clk_inv) + cr |= SCKP; + if (rdai->frm_clk_inv) + cr |= SWSP; + if (rdai->data_alignment) + cr |= SDTA; + if (rdai->sys_delay) + cr |= DEL; + if (rsnd_dai_is_play(rdai, io)) + cr |= TRMD; + + /* + * set ssi parameter + */ + ssi->rdai = rdai; + ssi->io = io; + ssi->cr_own = cr; + ssi->err = -1; /* ignore 1st error */ + + rsnd_ssi_mode_set(ssi); + + dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_ssi_quit(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + if (ssi->err > 0) + dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err); + + ssi->rdai = NULL; + ssi->io = NULL; + ssi->cr_own = 0; + ssi->err = 0; + + return 0; +} + +static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) +{ + /* under/over flow error */ + if (status & (UIRQ | OIRQ)) { + ssi->err++; + + /* clear error status */ + rsnd_mod_write(&ssi->mod, SSISR, 0); + } +} + +/* + * SSI PIO + */ +static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) +{ + struct rsnd_ssi *ssi = data; + struct rsnd_dai_stream *io = ssi->io; + u32 status = rsnd_mod_read(&ssi->mod, SSISR); + irqreturn_t ret = IRQ_NONE; + + if (io && (status & DIRQ)) { + struct rsnd_dai *rdai = ssi->rdai; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 *buf = (u32 *)(runtime->dma_area + + rsnd_dai_pointer_offset(io, 0)); + + rsnd_ssi_record_error(ssi, status); + + /* + * 8/16/32 data can be assesse to TDR/RDR register + * directly as 32bit data + * see rsnd_ssi_init() + */ + if (rsnd_dai_is_play(rdai, io)) + rsnd_mod_write(&ssi->mod, SSITDR, *buf); + else + *buf = rsnd_mod_read(&ssi->mod, SSIRDR); + + rsnd_dai_pointer_update(io, sizeof(*buf)); + + ret = IRQ_HANDLED; + } + + return ret; +} + +static int rsnd_ssi_pio_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + /* enable PIO IRQ */ + ssi->cr_etc = UIEN | OIEN | DIEN; + + rsnd_ssi_hw_start(ssi, rdai, io); + + dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; +} + +static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + + dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + ssi->cr_etc = 0; + + rsnd_ssi_hw_stop(ssi, rdai); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_pio_ops = { + .name = "ssi (pio)", + .init = rsnd_ssi_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_pio_start, + .stop = rsnd_ssi_pio_stop, +}; + +/* + * Non SSI + */ +static int rsnd_ssi_non(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "%s\n", __func__); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_non_ops = { + .name = "ssi (non)", + .init = rsnd_ssi_non, + .quit = rsnd_ssi_non, + .start = rsnd_ssi_non, + .stop = rsnd_ssi_non, +}; + +/* + * ssi mod function + */ +struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) +{ + BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv)); + + return &(((struct rsnd_ssiu *)(priv->ssiu))->ssi + id)->mod; +} + +int rsnd_ssi_probe(struct platform_device *pdev, + struct rcar_snd_info *info, + struct rsnd_priv *priv) +{ + struct rsnd_ssi_platform_info *pinfo; + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_mod_ops *ops; + struct clk *clk; + struct rsnd_ssiu *ssiu; + struct rsnd_ssi *ssi; + char name[RSND_SSI_NAME_SIZE]; + int i, nr, ret; + + /* + * init SSI + */ + nr = info->ssi_info_nr; + ssiu = devm_kzalloc(dev, sizeof(*ssiu) + (sizeof(*ssi) * nr), + GFP_KERNEL); + if (!ssiu) { + dev_err(dev, "SSI allocate failed\n"); + return -ENOMEM; + } + + priv->ssiu = ssiu; + ssiu->ssi = (struct rsnd_ssi *)(ssiu + 1); + ssiu->ssi_nr = nr; + + for_each_rsnd_ssi(ssi, priv, i) { + pinfo = &info->ssi_info[i]; + + snprintf(name, RSND_SSI_NAME_SIZE, "ssi.%d", i); + + clk = clk_get(dev, name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ssi->info = pinfo; + ssi->clk = clk; + + ops = &rsnd_ssi_non_ops; + + /* + * SSI PIO case + */ + if (rsnd_ssi_is_pio(ssi)) { + ret = devm_request_irq(dev, pinfo->pio_irq, + &rsnd_ssi_pio_interrupt, + IRQF_SHARED, + dev_name(dev), ssi); + if (ret) { + dev_err(dev, "SSI request interrupt failed\n"); + return ret; + } + + ops = &rsnd_ssi_pio_ops; + } + + rsnd_mod_init(priv, &ssi->mod, ops, i); + } + + rsnd_ssi_mode_init(priv, ssiu); + + dev_dbg(dev, "ssi probed\n"); + + return 0; +} + +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi; + int i; + + for_each_rsnd_ssi(ssi, priv, i) + clk_put(ssi->clk); +} -- cgit v1.2.3-71-gd317 From bf640876e21fe603f7f52b0c27d66b7716da0384 Mon Sep 17 00:00:00 2001 From: Dominik Dingel Date: Fri, 26 Jul 2013 15:04:07 +0200 Subject: KVM: s390: Make KVM_HVA_ERR_BAD usable on s390 Current common code uses PAGE_OFFSET to indicate a bad host virtual address. As this check won't work on architectures that don't map kernel and user memory into the same address space (e.g. s390), such architectures can now provide their own KVM_HVA_ERR_BAD defines. Signed-off-by: Dominik Dingel Signed-off-by: Christian Borntraeger Signed-off-by: Paolo Bonzini --- arch/s390/include/asm/kvm_host.h | 8 ++++++++ include/linux/kvm_host.h | 8 ++++++++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 3238d4004e84..e87ecaa2c569 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -274,6 +274,14 @@ struct kvm_arch{ int css_support; }; +#define KVM_HVA_ERR_BAD (-1UL) +#define KVM_HVA_ERR_RO_BAD (-2UL) + +static inline bool kvm_is_error_hva(unsigned long addr) +{ + return IS_ERR_VALUE(addr); +} + extern int sie64a(struct kvm_s390_sie_block *, u64 *); extern char sie_exit; #endif diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index c11c7686ae5f..ca645a01d37a 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -85,6 +85,12 @@ static inline bool is_noslot_pfn(pfn_t pfn) return pfn == KVM_PFN_NOSLOT; } +/* + * architectures with KVM_HVA_ERR_BAD other than PAGE_OFFSET (e.g. s390) + * provide own defines and kvm_is_error_hva + */ +#ifndef KVM_HVA_ERR_BAD + #define KVM_HVA_ERR_BAD (PAGE_OFFSET) #define KVM_HVA_ERR_RO_BAD (PAGE_OFFSET + PAGE_SIZE) @@ -93,6 +99,8 @@ static inline bool kvm_is_error_hva(unsigned long addr) return addr >= PAGE_OFFSET; } +#endif + #define KVM_ERR_PTR_BAD_PAGE (ERR_PTR(-ENOENT)) static inline bool is_error_page(struct page *page) -- cgit v1.2.3-71-gd317 From fe6f700d6cbb7e8a61711e325f53d9c9e0a42a4c Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Sun, 28 Jul 2013 18:54:21 +0300 Subject: net/mlx4_core: Respond to operation request by firmware This commit adds new firmware command and new firmware event. The firmware raises the MLX4_EVENT_TYPE_OP_REQUIRED event in order to signal the driver it needs to perform an administrative operation throughout the MLX4_CMD_GET_OP_REQ command. At the moment the supported operation is adding/removing multicast entries which are used by the firmware for handling NCSI traffic in B0 steering mode. Also, had to swap the order of mlx4_init_mcg_table() and mlx4_init_eq_table() to make sure that driver will get events only after resources are initialized to handle it. Signed-off-by: Yevgeny Petrilin Signed-off-by: Jack Morgenstein Signed-off-by: Eugenia Emantayev Signed-off-by: Amir Vadai Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 18 ++++++ drivers/net/ethernet/mellanox/mlx4/eq.c | 9 +++ drivers/net/ethernet/mellanox/mlx4/fw.c | 104 ++++++++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/fw.h | 1 + drivers/net/ethernet/mellanox/mlx4/main.c | 35 +++++----- drivers/net/ethernet/mellanox/mlx4/mcg.c | 11 ---- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 13 ++++ include/linux/mlx4/cmd.h | 1 + include/linux/mlx4/device.h | 1 + 9 files changed, 166 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 299d0184f983..141322c31ae9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -809,6 +809,15 @@ int MLX4_CMD_UPDATE_QP_wrapper(struct mlx4_dev *dev, int slave, return -EPERM; } +int MLX4_CMD_GET_OP_REQ_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd) +{ + return -EPERM; +} + int mlx4_DMA_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_vhcr *vhcr, struct mlx4_cmd_mailbox *inbox, @@ -1251,6 +1260,15 @@ static struct mlx4_cmd_info cmd_info[] = { .verify = NULL, .wrapper = MLX4_CMD_UPDATE_QP_wrapper }, + { + .opcode = MLX4_CMD_GET_OP_REQ, + .has_inbox = false, + .has_outbox = false, + .out_is_imm = false, + .encode_slave_id = false, + .verify = NULL, + .wrapper = MLX4_CMD_GET_OP_REQ_wrapper, + }, { .opcode = MLX4_CMD_CONF_SPECIAL_QP, .has_inbox = false, diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 7e042869ef0c..0416c5b3b35c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -79,6 +79,7 @@ enum { (1ull << MLX4_EVENT_TYPE_SRQ_QP_LAST_WQE) | \ (1ull << MLX4_EVENT_TYPE_SRQ_LIMIT) | \ (1ull << MLX4_EVENT_TYPE_CMD) | \ + (1ull << MLX4_EVENT_TYPE_OP_REQUIRED) | \ (1ull << MLX4_EVENT_TYPE_COMM_CHANNEL) | \ (1ull << MLX4_EVENT_TYPE_FLR_EVENT) | \ (1ull << MLX4_EVENT_TYPE_FATAL_WARNING)) @@ -629,6 +630,14 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) mlx4_warn(dev, "EQ overrun on EQN %d\n", eq->eqn); break; + case MLX4_EVENT_TYPE_OP_REQUIRED: + atomic_inc(&priv->opreq_count); + /* FW commands can't be executed from interrupt context + * working in deferred task + */ + queue_work(mlx4_wq, &priv->opreq_task); + break; + case MLX4_EVENT_TYPE_COMM_CHANNEL: if (!mlx4_is_master(dev)) { mlx4_warn(dev, "Received comm channel event " diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 8873d6802c80..aec6f5802da5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -1705,3 +1705,107 @@ int mlx4_wol_write(struct mlx4_dev *dev, u64 config, int port) MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); } EXPORT_SYMBOL_GPL(mlx4_wol_write); + +enum { + ADD_TO_MCG = 0x26, +}; + + +void mlx4_opreq_action(struct work_struct *work) +{ + struct mlx4_priv *priv = container_of(work, struct mlx4_priv, + opreq_task); + struct mlx4_dev *dev = &priv->dev; + int num_tasks = atomic_read(&priv->opreq_count); + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_mgm *mgm; + u32 *outbox; + u32 modifier; + u16 token; + u16 type_m; + u16 type; + int err; + u32 num_qps; + struct mlx4_qp qp; + int i; + u8 rem_mcg; + u8 prot; + +#define GET_OP_REQ_MODIFIER_OFFSET 0x08 +#define GET_OP_REQ_TOKEN_OFFSET 0x14 +#define GET_OP_REQ_TYPE_OFFSET 0x1a +#define GET_OP_REQ_DATA_OFFSET 0x20 + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) { + mlx4_err(dev, "Failed to allocate mailbox for GET_OP_REQ\n"); + return; + } + outbox = mailbox->buf; + + while (num_tasks) { + err = mlx4_cmd_box(dev, 0, mailbox->dma, 0, 0, + MLX4_CMD_GET_OP_REQ, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) { + mlx4_err(dev, "Failed to retreive required operation: %d\n", + err); + return; + } + MLX4_GET(modifier, outbox, GET_OP_REQ_MODIFIER_OFFSET); + MLX4_GET(token, outbox, GET_OP_REQ_TOKEN_OFFSET); + MLX4_GET(type, outbox, GET_OP_REQ_TYPE_OFFSET); + type_m = type >> 12; + type &= 0xfff; + + switch (type) { + case ADD_TO_MCG: + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { + mlx4_warn(dev, "ADD MCG operation is not supported in DEVICE_MANAGED steering mode\n"); + err = EPERM; + break; + } + mgm = (struct mlx4_mgm *)((u8 *)(outbox) + + GET_OP_REQ_DATA_OFFSET); + num_qps = be32_to_cpu(mgm->members_count) & + MGM_QPN_MASK; + rem_mcg = ((u8 *)(&mgm->members_count))[0] & 1; + prot = ((u8 *)(&mgm->members_count))[0] >> 6; + + for (i = 0; i < num_qps; i++) { + qp.qpn = be32_to_cpu(mgm->qp[i]); + if (rem_mcg) + err = mlx4_multicast_detach(dev, &qp, + mgm->gid, + prot, 0); + else + err = mlx4_multicast_attach(dev, &qp, + mgm->gid, + mgm->gid[5] + , 0, prot, + NULL); + if (err) + break; + } + break; + default: + mlx4_warn(dev, "Bad type for required operation\n"); + err = EINVAL; + break; + } + err = mlx4_cmd(dev, 0, ((u32) err | cpu_to_be32(token) << 16), + 1, MLX4_CMD_GET_OP_REQ, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) { + mlx4_err(dev, "Failed to acknowledge required request: %d\n", + err); + goto out; + } + memset(outbox, 0, 0xffc); + num_tasks = atomic_dec_return(&priv->opreq_count); + } + +out: + mlx4_free_cmd_mailbox(dev, mailbox); +} diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index fdf41665a059..a0a368b7c939 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -220,5 +220,6 @@ int mlx4_MAP_ICM_AUX(struct mlx4_dev *dev, struct mlx4_icm *icm); int mlx4_UNMAP_ICM_AUX(struct mlx4_dev *dev); int mlx4_NOP(struct mlx4_dev *dev); int mlx4_MOD_STAT_CFG(struct mlx4_dev *dev, struct mlx4_mod_stat_cfg *cfg); +void mlx4_opreq_action(struct work_struct *work); #endif /* MLX4_FW_H */ diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index e85af922dcdc..f1d818f9bb01 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -1692,11 +1692,19 @@ static int mlx4_setup_hca(struct mlx4_dev *dev) goto err_xrcd_table_free; } + if (!mlx4_is_slave(dev)) { + err = mlx4_init_mcg_table(dev); + if (err) { + mlx4_err(dev, "Failed to initialize multicast group table, aborting.\n"); + goto err_mr_table_free; + } + } + err = mlx4_init_eq_table(dev); if (err) { mlx4_err(dev, "Failed to initialize " "event queue table, aborting.\n"); - goto err_mr_table_free; + goto err_mcg_table_free; } err = mlx4_cmd_use_events(dev); @@ -1746,19 +1754,10 @@ static int mlx4_setup_hca(struct mlx4_dev *dev) goto err_srq_table_free; } - if (!mlx4_is_slave(dev)) { - err = mlx4_init_mcg_table(dev); - if (err) { - mlx4_err(dev, "Failed to initialize " - "multicast group table, aborting.\n"); - goto err_qp_table_free; - } - } - err = mlx4_init_counters_table(dev); if (err && err != -ENOENT) { mlx4_err(dev, "Failed to initialize counters table, aborting.\n"); - goto err_mcg_table_free; + goto err_qp_table_free; } if (!mlx4_is_slave(dev)) { @@ -1803,9 +1802,6 @@ static int mlx4_setup_hca(struct mlx4_dev *dev) err_counters_table_free: mlx4_cleanup_counters_table(dev); -err_mcg_table_free: - mlx4_cleanup_mcg_table(dev); - err_qp_table_free: mlx4_cleanup_qp_table(dev); @@ -1821,6 +1817,10 @@ err_cmd_poll: err_eq_table_free: mlx4_cleanup_eq_table(dev); +err_mcg_table_free: + if (!mlx4_is_slave(dev)) + mlx4_cleanup_mcg_table(dev); + err_mr_table_free: mlx4_cleanup_mr_table(dev); @@ -2197,6 +2197,9 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data) } } + atomic_set(&priv->opreq_count, 0); + INIT_WORK(&priv->opreq_task, mlx4_opreq_action); + /* * Now reset the HCA before we touch the PCI capabilities or * attempt a firmware command, since a boot ROM may have left @@ -2315,12 +2318,12 @@ err_port: mlx4_cleanup_port_info(&priv->port[port]); mlx4_cleanup_counters_table(dev); - mlx4_cleanup_mcg_table(dev); mlx4_cleanup_qp_table(dev); mlx4_cleanup_srq_table(dev); mlx4_cleanup_cq_table(dev); mlx4_cmd_use_polling(dev); mlx4_cleanup_eq_table(dev); + mlx4_cleanup_mcg_table(dev); mlx4_cleanup_mr_table(dev); mlx4_cleanup_xrcd_table(dev); mlx4_cleanup_pd_table(dev); @@ -2403,12 +2406,12 @@ static void mlx4_remove_one(struct pci_dev *pdev) RES_TR_FREE_SLAVES_ONLY); mlx4_cleanup_counters_table(dev); - mlx4_cleanup_mcg_table(dev); mlx4_cleanup_qp_table(dev); mlx4_cleanup_srq_table(dev); mlx4_cleanup_cq_table(dev); mlx4_cmd_use_polling(dev); mlx4_cleanup_eq_table(dev); + mlx4_cleanup_mcg_table(dev); mlx4_cleanup_mr_table(dev); mlx4_cleanup_xrcd_table(dev); mlx4_cleanup_pd_table(dev); diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index f3e804f2a35f..55f6245efb6c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -39,19 +39,8 @@ #include "mlx4.h" -#define MGM_QPN_MASK 0x00FFFFFF -#define MGM_BLCK_LB_BIT 30 - static const u8 zero_gid[16]; /* automatically initialized to 0 */ -struct mlx4_mgm { - __be32 next_gid_index; - __be32 members_count; - u32 reserved[2]; - u8 gid[16]; - __be32 qp[MLX4_MAX_QP_PER_MGM]; -}; - int mlx4_get_mgm_entry_size(struct mlx4_dev *dev) { return 1 << dev->oper_log_mgm_entry_size; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 17d9277e33ef..348bb8c7d9a7 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -554,6 +554,17 @@ struct mlx4_mfunc { struct mlx4_mfunc_master_ctx master; }; +#define MGM_QPN_MASK 0x00FFFFFF +#define MGM_BLCK_LB_BIT 30 + +struct mlx4_mgm { + __be32 next_gid_index; + __be32 members_count; + u32 reserved[2]; + u8 gid[16]; + __be32 qp[MLX4_MAX_QP_PER_MGM]; +}; + struct mlx4_cmd { struct pci_pool *pool; void __iomem *hcr; @@ -802,6 +813,8 @@ struct mlx4_priv { u8 virt2phys_pkey[MLX4_MFUNC_MAX][MLX4_MAX_PORTS][MLX4_MAX_PORT_PKEYS]; __be64 slave_node_guids[MLX4_MFUNC_MAX]; + atomic_t opreq_count; + struct work_struct opreq_task; }; static inline struct mlx4_priv *mlx4_priv(struct mlx4_dev *dev) diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index bb1c8096a7eb..cd1fdf75103b 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h @@ -69,6 +69,7 @@ enum { MLX4_CMD_SET_ICM_SIZE = 0xffd, /*master notify fw on finish for slave's flr*/ MLX4_CMD_INFORM_FLR_DONE = 0x5b, + MLX4_CMD_GET_OP_REQ = 0x59, /* TPT commands */ MLX4_CMD_SW2HW_MPT = 0xd, diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 52c23a892bab..6aebdfe0ed8b 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -207,6 +207,7 @@ enum mlx4_event { MLX4_EVENT_TYPE_CMD = 0x0a, MLX4_EVENT_TYPE_VEP_UPDATE = 0x19, MLX4_EVENT_TYPE_COMM_CHANNEL = 0x18, + MLX4_EVENT_TYPE_OP_REQUIRED = 0x1a, MLX4_EVENT_TYPE_FATAL_WARNING = 0x1b, MLX4_EVENT_TYPE_FLR_EVENT = 0x1c, MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT = 0x1d, -- cgit v1.2.3-71-gd317 From 410aee70f06da60dd8f78c3ec3346cc74c9bed46 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sun, 30 Jun 2013 15:27:26 +0300 Subject: usb: phy: protect against NULL phy pointers In order to decrease the amount of work done by PHY users, allow NULL phy pointers to be passed. Signed-off-by: Felipe Balbi --- include/linux/usb/phy.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index 44036808bf0f..6c0b1c513db7 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -142,7 +142,7 @@ extern void usb_remove_phy(struct usb_phy *); /* helpers for direct access thru low-level io interface */ static inline int usb_phy_io_read(struct usb_phy *x, u32 reg) { - if (x->io_ops && x->io_ops->read) + if (x && x->io_ops && x->io_ops->read) return x->io_ops->read(x, reg); return -EINVAL; @@ -150,7 +150,7 @@ static inline int usb_phy_io_read(struct usb_phy *x, u32 reg) static inline int usb_phy_io_write(struct usb_phy *x, u32 val, u32 reg) { - if (x->io_ops && x->io_ops->write) + if (x && x->io_ops && x->io_ops->write) return x->io_ops->write(x, val, reg); return -EINVAL; @@ -159,7 +159,7 @@ static inline int usb_phy_io_write(struct usb_phy *x, u32 val, u32 reg) static inline int usb_phy_init(struct usb_phy *x) { - if (x->init) + if (x && x->init) return x->init(x); return 0; @@ -168,14 +168,14 @@ usb_phy_init(struct usb_phy *x) static inline void usb_phy_shutdown(struct usb_phy *x) { - if (x->shutdown) + if (x && x->shutdown) x->shutdown(x); } static inline int usb_phy_vbus_on(struct usb_phy *x) { - if (!x->set_vbus) + if (!x || !x->set_vbus) return 0; return x->set_vbus(x, true); @@ -184,7 +184,7 @@ usb_phy_vbus_on(struct usb_phy *x) static inline int usb_phy_vbus_off(struct usb_phy *x) { - if (!x->set_vbus) + if (!x || !x->set_vbus) return 0; return x->set_vbus(x, false); @@ -258,7 +258,7 @@ usb_phy_set_power(struct usb_phy *x, unsigned mA) static inline int usb_phy_set_suspend(struct usb_phy *x, int suspend) { - if (x->set_suspend != NULL) + if (x && x->set_suspend != NULL) return x->set_suspend(x, suspend); else return 0; @@ -267,7 +267,7 @@ usb_phy_set_suspend(struct usb_phy *x, int suspend) static inline int usb_phy_notify_connect(struct usb_phy *x, enum usb_device_speed speed) { - if (x->notify_connect) + if (x && x->notify_connect) return x->notify_connect(x, speed); else return 0; @@ -276,7 +276,7 @@ usb_phy_notify_connect(struct usb_phy *x, enum usb_device_speed speed) static inline int usb_phy_notify_disconnect(struct usb_phy *x, enum usb_device_speed speed) { - if (x->notify_disconnect) + if (x && x->notify_disconnect) return x->notify_disconnect(x, speed); else return 0; -- cgit v1.2.3-71-gd317 From 1494a1f62bf7cf57345e9282c8189fe2a21fab64 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sun, 30 Jun 2013 13:56:45 +0300 Subject: usb: common: introduce of_usb_get_maximum_speed() this helper will be used for controllers which want to work at a lower speed even though they support higher USB transfer rates. One such case is Texas Instruments' AM437x SoC where it uses a USB3 controller without a USB3 PHY, rendering the controller USB2-only. Signed-off-by: Felipe Balbi --- Documentation/devicetree/bindings/usb/generic.txt | 24 +++++++++++ drivers/usb/usb-common.c | 49 ++++++++++++++++++----- include/linux/usb/of.h | 8 ++++ 3 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 Documentation/devicetree/bindings/usb/generic.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/usb/generic.txt b/Documentation/devicetree/bindings/usb/generic.txt new file mode 100644 index 000000000000..477d5bb5e51c --- /dev/null +++ b/Documentation/devicetree/bindings/usb/generic.txt @@ -0,0 +1,24 @@ +Generic USB Properties + +Optional properties: + - maximum-speed: tells USB controllers we want to work up to a certain + speed. Valid arguments are "super-speed", "high-speed", + "full-speed" and "low-speed". In case this isn't passed + via DT, USB controllers should default to their maximum + HW capability. + - dr_mode: tells Dual-Role USB controllers that we want to work on a + particular mode. Valid arguments are "host", + "peripheral" and "otg". In case this attribute isn't + passed via DT, USB DRD controllers should default to + OTG. + +This is an attribute to a USB controller such as: + +dwc3@4a030000 { + compatible = "synopsys,dwc3"; + reg = <0x4a030000 0xcfff>; + interrupts = <0 92 4> + usb-phy = <&usb2_phy>, <&usb3,phy>; + maximum-speed = "super-speed"; + dr_mode = "otg"; +}; diff --git a/drivers/usb/usb-common.c b/drivers/usb/usb-common.c index 675384dabfe9..d771870a819e 100644 --- a/drivers/usb/usb-common.c +++ b/drivers/usb/usb-common.c @@ -43,20 +43,20 @@ const char *usb_otg_state_string(enum usb_otg_state state) } EXPORT_SYMBOL_GPL(usb_otg_state_string); +static const char *const speed_names[] = { + [USB_SPEED_UNKNOWN] = "UNKNOWN", + [USB_SPEED_LOW] = "low-speed", + [USB_SPEED_FULL] = "full-speed", + [USB_SPEED_HIGH] = "high-speed", + [USB_SPEED_WIRELESS] = "wireless", + [USB_SPEED_SUPER] = "super-speed", +}; + const char *usb_speed_string(enum usb_device_speed speed) { - static const char *const names[] = { - [USB_SPEED_UNKNOWN] = "UNKNOWN", - [USB_SPEED_LOW] = "low-speed", - [USB_SPEED_FULL] = "full-speed", - [USB_SPEED_HIGH] = "high-speed", - [USB_SPEED_WIRELESS] = "wireless", - [USB_SPEED_SUPER] = "super-speed", - }; - - if (speed < 0 || speed >= ARRAY_SIZE(names)) + if (speed < 0 || speed >= ARRAY_SIZE(speed_names)) speed = USB_SPEED_UNKNOWN; - return names[speed]; + return speed_names[speed]; } EXPORT_SYMBOL_GPL(usb_speed_string); @@ -112,6 +112,33 @@ enum usb_dr_mode of_usb_get_dr_mode(struct device_node *np) return USB_DR_MODE_UNKNOWN; } EXPORT_SYMBOL_GPL(of_usb_get_dr_mode); + +/** + * of_usb_get_maximum_speed - Get maximum requested speed for a given USB + * controller. + * @np: Pointer to the given device_node + * + * The function gets the maximum speed string from property "maximum-speed", + * and returns the corresponding enum usb_device_speed. + */ +enum usb_device_speed of_usb_get_maximum_speed(struct device_node *np) +{ + const char *maximum_speed; + int err; + int i; + + err = of_property_read_string(np, "maximum-speed", &maximum_speed); + if (err < 0) + return USB_SPEED_UNKNOWN; + + for (i = 0; i < ARRAY_SIZE(speed_names); i++) + if (strcmp(maximum_speed, speed_names[i]) == 0) + return i; + + return USB_SPEED_UNKNOWN; +} +EXPORT_SYMBOL_GPL(of_usb_get_maximum_speed); + #endif MODULE_LICENSE("GPL"); diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index a0ef405368b8..5a7cb9ebf0ef 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -7,16 +7,24 @@ #ifndef __LINUX_USB_OF_H #define __LINUX_USB_OF_H +#include #include #include #if IS_ENABLED(CONFIG_OF) enum usb_dr_mode of_usb_get_dr_mode(struct device_node *np); +enum usb_device_speed of_usb_get_maximum_speed(struct device_node *np); #else static inline enum usb_dr_mode of_usb_get_dr_mode(struct device_node *np) { return USB_DR_MODE_UNKNOWN; } + +static inline enum usb_device_speed +of_usb_get_maximum_speed(struct device_node *np) +{ + return USB_SPEED_UNKNOWN; +} #endif #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_USB_PHY) -- cgit v1.2.3-71-gd317 From 5702f75375aa9ecf8ad3431aef3fe6ce8c8dbd15 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 17 Jul 2013 11:09:49 +0300 Subject: usb: gadget: udc-core: move sysfs_notify() to a workqueue usb_gadget_set_state() will call sysfs_notify() which might sleep. Some users might want to call usb_gadget_set_state() from the very IRQ handler which actually changes the gadget state. Instead of having every UDC driver add their own workqueue for such a simple notification, we're adding it generically to our struct usb_gadget, so the details are hidden from all UDC drivers. Acked-by: Alan Stern Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc-core.c | 12 +++++++++++- include/linux/usb/gadget.h | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index c28ac9872030..3122ab942f75 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -105,11 +106,18 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); /* ------------------------------------------------------------------------- */ +static void usb_gadget_state_work(struct work_struct *work) +{ + struct usb_gadget *gadget = work_to_gadget(work); + + sysfs_notify(&gadget->dev.kobj, NULL, "status"); +} + void usb_gadget_set_state(struct usb_gadget *gadget, enum usb_device_state state) { gadget->state = state; - sysfs_notify(&gadget->dev.kobj, NULL, "status"); + schedule_work(&gadget->work); } EXPORT_SYMBOL_GPL(usb_gadget_set_state); @@ -196,6 +204,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, goto err1; dev_set_name(&gadget->dev, "gadget"); + INIT_WORK(&gadget->work, usb_gadget_state_work); gadget->dev.parent = parent; #ifdef CONFIG_HAS_DMA @@ -315,6 +324,7 @@ found: usb_gadget_remove_driver(udc); kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); + flush_work(&gadget->work); device_unregister(&udc->dev); device_unregister(&gadget->dev); } diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index f1b0dca60f12..942ef5e053bf 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -22,6 +22,7 @@ #include #include #include +#include #include struct usb_ep; @@ -475,6 +476,7 @@ struct usb_gadget_ops { /** * struct usb_gadget - represents a usb slave device + * @work: (internal use) Workqueue to be used for sysfs_notify() * @ops: Function pointers used to access hardware-specific operations. * @ep0: Endpoint zero, used when reading or writing responses to * driver setup() requests @@ -520,6 +522,7 @@ struct usb_gadget_ops { * device is acting as a B-Peripheral (so is_a_peripheral is false). */ struct usb_gadget { + struct work_struct work; /* readonly to gadget driver */ const struct usb_gadget_ops *ops; struct usb_ep *ep0; @@ -538,6 +541,7 @@ struct usb_gadget { unsigned out_epnum; unsigned in_epnum; }; +#define work_to_gadget(w) (container_of((w), struct usb_gadget, work)) static inline void set_gadget_data(struct usb_gadget *gadget, void *data) { dev_set_drvdata(&gadget->dev, data); } -- cgit v1.2.3-71-gd317 From f5b8c8b6d3b4697f28b818d8784e3e4b2a290022 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Wed, 17 Jul 2013 10:37:49 +0300 Subject: usb: tegra: Use regulators instead of GPIOs for USB PHY VBUS The tegra ehci driver has enabled USB vbus regulators directly using GPIOs and the device tree attribute nvidia,vbus-gpio. This is ugly and causes error messages on boot when both the regulator driver and the ehci driver want access to the same GPIO. After this patch, usb vbus regulators for tegra usb phy devices are specified with the device tree attribute vbus-supply = <&x> where x is a regulator defined in the device tree. The old nvidia,vbus-gpio property is no longer supported. Signed-off-by: Mikko Perttunen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/host/ehci-tegra.c | 34 +--------------------------------- drivers/usb/phy/phy-tegra-usb.c | 24 ++++++++++++++++++++++++ include/linux/usb/tegra_usb_phy.h | 1 + 3 files changed, 26 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 6ee7ef79b4f8..14c1f35a4b10 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -322,33 +322,6 @@ static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) free_dma_aligned_buffer(urb); } -static int setup_vbus_gpio(struct platform_device *pdev, - struct tegra_ehci_platform_data *pdata) -{ - int err = 0; - int gpio; - - gpio = pdata->vbus_gpio; - if (!gpio_is_valid(gpio)) - gpio = of_get_named_gpio(pdev->dev.of_node, - "nvidia,vbus-gpio", 0); - if (!gpio_is_valid(gpio)) - return 0; - - err = gpio_request(gpio, "vbus_gpio"); - if (err) { - dev_err(&pdev->dev, "can't request vbus gpio %d", gpio); - return err; - } - err = gpio_direction_output(gpio, 1); - if (err) { - dev_err(&pdev->dev, "can't enable vbus\n"); - return err; - } - - return err; -} - static int tegra_ehci_probe(struct platform_device *pdev) { struct resource *res; @@ -376,14 +349,11 @@ static int tegra_ehci_probe(struct platform_device *pdev) if (!pdev->dev.coherent_dma_mask) pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - setup_vbus_gpio(pdev, pdata); - hcd = usb_create_hcd(&tegra_ehci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { dev_err(&pdev->dev, "Unable to create HCD\n"); - err = -ENOMEM; - goto cleanup_vbus_gpio; + return -ENOMEM; } platform_set_drvdata(pdev, hcd); ehci = hcd_to_ehci(hcd); @@ -494,8 +464,6 @@ cleanup_clk_get: clk_put(tegra->clk); cleanup_hcd_create: usb_put_hcd(hcd); -cleanup_vbus_gpio: - /* FIXME: Undo setup_vbus_gpio() here */ return err; } diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 3ecd49871b88..d92a63a5b0c9 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -34,6 +34,7 @@ #include #include #include +#include #define ULPI_VIEWPORT 0x170 @@ -613,6 +614,9 @@ static void tegra_usb_phy_close(struct usb_phy *x) { struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy); + if (!IS_ERR(phy->vbus)) + regulator_disable(phy->vbus); + clk_disable_unprepare(phy->pll_u); } @@ -705,6 +709,16 @@ static int tegra_usb_phy_init(struct tegra_usb_phy *phy) goto fail; } + if (!IS_ERR(phy->vbus)) { + err = regulator_enable(phy->vbus); + if (err) { + dev_err(phy->dev, + "failed to enable usb vbus regulator: %d\n", + err); + goto fail; + } + } + if (phy->is_ulpi_phy) err = ulpi_open(phy); else @@ -896,6 +910,16 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) } else tegra_phy->mode = TEGRA_USB_PHY_MODE_OTG; + /* On some boards, the VBUS regulator doesn't need to be controlled */ + if (of_find_property(np, "vbus-supply", NULL)) { + tegra_phy->vbus = devm_regulator_get(&pdev->dev, "vbus"); + if (IS_ERR(tegra_phy->vbus)) + return PTR_ERR(tegra_phy->vbus); + } else { + dev_notice(&pdev->dev, "no vbus regulator"); + tegra_phy->vbus = ERR_PTR(-ENODEV); + } + tegra_phy->dev = &pdev->dev; err = tegra_usb_phy_init(tegra_phy); if (err < 0) diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index d2ca919a5b73..2b5fa947980a 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -55,6 +55,7 @@ struct tegra_usb_phy { struct clk *clk; struct clk *pll_u; struct clk *pad_clk; + struct regulator *vbus; enum tegra_usb_phy_mode mode; void *config; struct usb_phy *ulpi; -- cgit v1.2.3-71-gd317 From 185d0fd570c121c0c3d4527842a097e50e3e7a79 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 21:38:01 +0300 Subject: usb: phy: tegra: Remove unnecessary 'dev' field struct usb_phy already has a field for the device pointer, so this unnecessary field can be removed. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 27 +++++++++++++++------------ include/linux/usb/tegra_usb_phy.h | 1 - 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index d92a63a5b0c9..311fe03f16e1 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -211,7 +211,7 @@ static void set_phcd(struct tegra_usb_phy *phy, bool enable) static int utmip_pad_open(struct tegra_usb_phy *phy) { - phy->pad_clk = devm_clk_get(phy->dev, "utmi-pads"); + phy->pad_clk = devm_clk_get(phy->u_phy.dev, "utmi-pads"); if (IS_ERR(phy->pad_clk)) { pr_err("%s: can't get utmip pad clock\n", __func__); return PTR_ERR(phy->pad_clk); @@ -540,13 +540,15 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) ret = gpio_direction_output(phy->reset_gpio, 0); if (ret < 0) { - dev_err(phy->dev, "gpio %d not set to 0\n", phy->reset_gpio); + dev_err(phy->u_phy.dev, "gpio %d not set to 0\n", + phy->reset_gpio); return ret; } msleep(5); ret = gpio_direction_output(phy->reset_gpio, 1); if (ret < 0) { - dev_err(phy->dev, "gpio %d not set to 1\n", phy->reset_gpio); + dev_err(phy->u_phy.dev, "gpio %d not set to 1\n", + phy->reset_gpio); return ret; } @@ -649,29 +651,30 @@ static int ulpi_open(struct tegra_usb_phy *phy) { int err; - phy->clk = devm_clk_get(phy->dev, "ulpi-link"); + phy->clk = devm_clk_get(phy->u_phy.dev, "ulpi-link"); if (IS_ERR(phy->clk)) { pr_err("%s: can't get ulpi clock\n", __func__); return PTR_ERR(phy->clk); } - err = devm_gpio_request(phy->dev, phy->reset_gpio, "ulpi_phy_reset_b"); + err = devm_gpio_request(phy->u_phy.dev, phy->reset_gpio, + "ulpi_phy_reset_b"); if (err < 0) { - dev_err(phy->dev, "request failed for gpio: %d\n", + dev_err(phy->u_phy.dev, "request failed for gpio: %d\n", phy->reset_gpio); return err; } err = gpio_direction_output(phy->reset_gpio, 0); if (err < 0) { - dev_err(phy->dev, "gpio %d direction not set to output\n", + dev_err(phy->u_phy.dev, "gpio %d direction not set to output\n", phy->reset_gpio); return err; } phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); if (!phy->ulpi) { - dev_err(phy->dev, "otg_ulpi_create returned NULL\n"); + dev_err(phy->u_phy.dev, "otg_ulpi_create returned NULL\n"); err = -ENOMEM; return err; } @@ -686,7 +689,7 @@ static int tegra_usb_phy_init(struct tegra_usb_phy *phy) int i; int err; - phy->pll_u = devm_clk_get(phy->dev, "pll_u"); + phy->pll_u = devm_clk_get(phy->u_phy.dev, "pll_u"); if (IS_ERR(phy->pll_u)) { pr_err("Can't get pll_u clock\n"); return PTR_ERR(phy->pll_u); @@ -712,7 +715,7 @@ static int tegra_usb_phy_init(struct tegra_usb_phy *phy) if (!IS_ERR(phy->vbus)) { err = regulator_enable(phy->vbus); if (err) { - dev_err(phy->dev, + dev_err(phy->u_phy.dev, "failed to enable usb vbus regulator: %d\n", err); goto fail; @@ -920,7 +923,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) tegra_phy->vbus = ERR_PTR(-ENODEV); } - tegra_phy->dev = &pdev->dev; + tegra_phy->u_phy.dev = &pdev->dev; err = tegra_usb_phy_init(tegra_phy); if (err < 0) return err; @@ -953,7 +956,7 @@ static int tegra_usb_phy_match(struct device *dev, void *data) struct tegra_usb_phy *tegra_phy = dev_get_drvdata(dev); struct device_node *dn = data; - return (tegra_phy->dev->of_node == dn) ? 1 : 0; + return (tegra_phy->u_phy.dev->of_node == dn) ? 1 : 0; } struct usb_phy *tegra_usb_get_phy(struct device_node *dn) diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 2b5fa947980a..c2bc7106aaa8 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -60,7 +60,6 @@ struct tegra_usb_phy { void *config; struct usb_phy *ulpi; struct usb_phy u_phy; - struct device *dev; bool is_legacy_phy; bool is_ulpi_phy; int reset_gpio; -- cgit v1.2.3-71-gd317 From 5fed6828318119656e243c4f0a11955cefc8eebd Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 21:38:04 +0300 Subject: ARM: tegra: Remove USB platform data USB-related platform data is not used anymore in the Tegra USB drivers, so remove all of it. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Felipe Balbi --- arch/arm/mach-tegra/tegra.c | 38 +-------------------------------- include/linux/platform_data/tegra_usb.h | 32 --------------------------- include/linux/usb/tegra_usb_phy.h | 5 ----- 3 files changed, 1 insertion(+), 74 deletions(-) delete mode 100644 include/linux/platform_data/tegra_usb.h (limited to 'include') diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index 0d1e4128d460..fc97cfd52769 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -46,40 +45,6 @@ #include "fuse.h" #include "iomap.h" -static struct tegra_ehci_platform_data tegra_ehci1_pdata = { - .operating_mode = TEGRA_USB_OTG, - .power_down_on_bus_suspend = 1, - .vbus_gpio = -1, -}; - -static struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config = { - .reset_gpio = -1, - .clk = "cdev2", -}; - -static struct tegra_ehci_platform_data tegra_ehci2_pdata = { - .phy_config = &tegra_ehci2_ulpi_phy_config, - .operating_mode = TEGRA_USB_HOST, - .power_down_on_bus_suspend = 1, - .vbus_gpio = -1, -}; - -static struct tegra_ehci_platform_data tegra_ehci3_pdata = { - .operating_mode = TEGRA_USB_HOST, - .power_down_on_bus_suspend = 1, - .vbus_gpio = -1, -}; - -static struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = { - OF_DEV_AUXDATA("nvidia,tegra20-ehci", 0xC5000000, "tegra-ehci.0", - &tegra_ehci1_pdata), - OF_DEV_AUXDATA("nvidia,tegra20-ehci", 0xC5004000, "tegra-ehci.1", - &tegra_ehci2_pdata), - OF_DEV_AUXDATA("nvidia,tegra20-ehci", 0xC5008000, "tegra-ehci.2", - &tegra_ehci3_pdata), - {} -}; - static void __init tegra_dt_init(void) { struct soc_device_attribute *soc_dev_attr; @@ -112,8 +77,7 @@ static void __init tegra_dt_init(void) * devices */ out: - of_platform_populate(NULL, of_default_bus_match_table, - tegra20_auxdata_lookup, parent); + of_platform_populate(NULL, of_default_bus_match_table, NULL, parent); } static void __init trimslice_init(void) diff --git a/include/linux/platform_data/tegra_usb.h b/include/linux/platform_data/tegra_usb.h deleted file mode 100644 index 66c673fef408..000000000000 --- a/include/linux/platform_data/tegra_usb.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2010 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef _TEGRA_USB_H_ -#define _TEGRA_USB_H_ - -enum tegra_usb_operating_modes { - TEGRA_USB_DEVICE, - TEGRA_USB_HOST, - TEGRA_USB_OTG, -}; - -struct tegra_ehci_platform_data { - enum tegra_usb_operating_modes operating_mode; - /* power down the phy on bus suspend */ - int power_down_on_bus_suspend; - void *phy_config; - int vbus_gpio; -}; - -#endif /* _TEGRA_USB_H_ */ diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index c2bc7106aaa8..847415313ad2 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -28,11 +28,6 @@ struct tegra_utmip_config { u8 xcvr_lsrslew; }; -struct tegra_ulpi_config { - int reset_gpio; - const char *clk; -}; - enum tegra_usb_phy_port_speed { TEGRA_USB_PHY_PORT_SPEED_FULL = 0, TEGRA_USB_PHY_PORT_SPEED_LOW, -- cgit v1.2.3-71-gd317 From 3b102e8bc0e49d417c7d376af857537080335dbf Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 21:38:07 +0300 Subject: usb: phy: tegra: Remove custom PHY locating APIs The Tegra EHCI driver is no longer using these custom functions, so they can be removed. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 24 ------------------------ include/linux/usb/tegra_usb_phy.h | 2 -- 2 files changed, 26 deletions(-) (limited to 'include') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 441c306c050f..fb5bc8cea680 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -968,29 +968,5 @@ static struct platform_driver tegra_usb_phy_driver = { }; module_platform_driver(tegra_usb_phy_driver); -static int tegra_usb_phy_match(struct device *dev, void *data) -{ - struct tegra_usb_phy *tegra_phy = dev_get_drvdata(dev); - struct device_node *dn = data; - - return (tegra_phy->u_phy.dev->of_node == dn) ? 1 : 0; -} - -struct usb_phy *tegra_usb_get_phy(struct device_node *dn) -{ - struct device *dev; - struct tegra_usb_phy *tegra_phy; - - dev = driver_find_device(&tegra_usb_phy_driver.driver, NULL, dn, - tegra_usb_phy_match); - if (!dev) - return ERR_PTR(-EPROBE_DEFER); - - tegra_phy = dev_get_drvdata(dev); - - return &tegra_phy->u_phy; -} -EXPORT_SYMBOL_GPL(tegra_usb_get_phy); - MODULE_DESCRIPTION("Tegra USB PHY driver"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 847415313ad2..a095c98184d0 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -60,8 +60,6 @@ struct tegra_usb_phy { int reset_gpio; }; -struct usb_phy *tegra_usb_get_phy(struct device_node *dn); - void tegra_usb_phy_preresume(struct usb_phy *phy); void tegra_usb_phy_postresume(struct usb_phy *phy); -- cgit v1.2.3-71-gd317 From 6558d7edbe069df1220b525362d4aa854a65a8bc Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Thu, 25 Jul 2013 21:38:09 +0300 Subject: usb: phy: tegra: Use DT helpers for dr_mode Use the new of_usb_get_dr_mode helper function for parsing dr_mode from the device tree. Also replace the usage of the custom tegra_usb_phy_mode enum with the standard enum. Signed-off-by: Tuomas Tynkkynen Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 24 ++++++++++++------------ include/linux/usb/tegra_usb_phy.h | 8 +------- 2 files changed, 13 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index fb469f1e8d10..01c30ff8874e 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -377,7 +377,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); writel(val, base + UTMIP_PLL_CFG1); - if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { + if (phy->mode == USB_DR_MODE_PERIPHERAL) { val = readl(base + USB_SUSP_CTRL); val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV); writel(val, base + USB_SUSP_CTRL); @@ -412,7 +412,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) if (phy->is_legacy_phy) { val = readl(base + UTMIP_SPARE_CFG0); - if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) + if (phy->mode == USB_DR_MODE_PERIPHERAL) val &= ~FUSE_SETUP_SEL; else val |= FUSE_SETUP_SEL; @@ -453,7 +453,7 @@ static int utmi_phy_power_off(struct tegra_usb_phy *phy) utmi_phy_clk_disable(phy); - if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) { + if (phy->mode == USB_DR_MODE_PERIPHERAL) { val = readl(base + USB_SUSP_CTRL); val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0); val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5); @@ -907,15 +907,15 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) return -EINVAL; } - err = of_property_match_string(np, "dr_mode", "otg"); - if (err < 0) { - err = of_property_match_string(np, "dr_mode", "peripheral"); - if (err < 0) - tegra_phy->mode = TEGRA_USB_PHY_MODE_HOST; - else - tegra_phy->mode = TEGRA_USB_PHY_MODE_DEVICE; - } else - tegra_phy->mode = TEGRA_USB_PHY_MODE_OTG; + if (of_find_property(np, "dr_mode", NULL)) + tegra_phy->mode = of_usb_get_dr_mode(np); + else + tegra_phy->mode = USB_DR_MODE_HOST; + + if (tegra_phy->mode == USB_DR_MODE_UNKNOWN) { + dev_err(&pdev->dev, "dr_mode is invalid\n"); + return -EINVAL; + } /* On some boards, the VBUS regulator doesn't need to be controlled */ if (of_find_property(np, "vbus-supply", NULL)) { diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index a095c98184d0..d3db274610a1 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -34,12 +34,6 @@ enum tegra_usb_phy_port_speed { TEGRA_USB_PHY_PORT_SPEED_HIGH, }; -enum tegra_usb_phy_mode { - TEGRA_USB_PHY_MODE_DEVICE, - TEGRA_USB_PHY_MODE_HOST, - TEGRA_USB_PHY_MODE_OTG, -}; - struct tegra_xtal_freq; struct tegra_usb_phy { @@ -51,7 +45,7 @@ struct tegra_usb_phy { struct clk *pll_u; struct clk *pad_clk; struct regulator *vbus; - enum tegra_usb_phy_mode mode; + enum usb_dr_mode mode; void *config; struct usb_phy *ulpi; struct usb_phy u_phy; -- cgit v1.2.3-71-gd317 From 4fefd69853a4e83040ddaa98d3b6e5e12cc4f97a Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Mon, 29 Jul 2013 13:51:58 +0100 Subject: ASoC: core: Add snd_soc_card_get_kcontrol() This is useful for drivers who want to grab a pointer to snd_kcontrol outside of the kcontrol callbacks. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7ec15a..b33d1de46396 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -475,6 +475,8 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template, void *data, const char *long_name, const char *prefix); +struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, + const char *name); int snd_soc_add_codec_controls(struct snd_soc_codec *codec, const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_add_platform_controls(struct snd_soc_platform *platform, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0ec070cf7231..cef714effc1e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2299,6 +2299,22 @@ static int snd_soc_add_controls(struct snd_card *card, struct device *dev, return 0; } +struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card, + const char *name) +{ + struct snd_card *card = soc_card->snd_card; + struct snd_kcontrol *kctl; + + if (unlikely(!name)) + return NULL; + + list_for_each_entry(kctl, &card->controls, list) + if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) + return kctl; + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol); + /** * snd_soc_add_codec_controls - add an array of controls to a codec. * Convenience function to add a list of controls. Many codecs were -- cgit v1.2.3-71-gd317 From 49834de234f3cf592c3d242c889ca603db8e7050 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 28 Jul 2013 14:47:02 +0100 Subject: spi: Provide core support for runtime PM during transfers Most SPI drivers that implement runtime PM support use identical code to do so: they acquire a runtime PM lock in prepare_transfer_hardware() and then they release it in unprepare_transfer_hardware(). The variations in this are mostly missing error checking and the choice to use autosuspend. Since these runtime PM calls are normally the only thing in the prepare and unprepare callbacks and the autosuspend API transparently does the right thing on devices with autosuspend disabled factor all of this out into the core with a flag to enable the behaviour. Signed-off-by: Mark Brown Reviewed-by: Stephen Warren Acked-by: Linus Walleij --- drivers/spi/spi.c | 16 ++++++++++++++++ include/linux/spi/spi.h | 5 +++++ 2 files changed, 21 insertions(+) (limited to 'include') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 978dda2c5239..361cced68069 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -553,6 +553,10 @@ static void spi_pump_messages(struct kthread_work *work) master->unprepare_transfer_hardware(master)) dev_err(&master->dev, "failed to unprepare transfer hardware\n"); + if (master->auto_runtime_pm) { + pm_runtime_mark_last_busy(master->dev.parent); + pm_runtime_put_autosuspend(master->dev.parent); + } return; } @@ -572,11 +576,23 @@ static void spi_pump_messages(struct kthread_work *work) master->busy = true; spin_unlock_irqrestore(&master->queue_lock, flags); + if (!was_busy && master->auto_runtime_pm) { + ret = pm_runtime_get_sync(master->dev.parent); + if (ret < 0) { + dev_err(&master->dev, "Failed to power device: %d\n", + ret); + return; + } + } + if (!was_busy && master->prepare_transfer_hardware) { ret = master->prepare_transfer_hardware(master); if (ret) { dev_err(&master->dev, "failed to prepare transfer hardware\n"); + + if (master->auto_runtime_pm) + pm_runtime_put(master->dev.parent); return; } } diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 28e440be1c07..bf0204c00053 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -254,6 +254,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @busy: message pump is busy * @running: message pump is running * @rt: whether this queue is set to run as a realtime task + * @auto_runtime_pm: the core should ensure a runtime PM reference is held + * while the hardware is prepared, using the parent + * device for the spidev * @prepare_transfer_hardware: a message will soon arrive from the queue * so the subsystem requests the driver to prepare the transfer hardware * by issuing this call @@ -374,11 +377,13 @@ struct spi_master { bool busy; bool running; bool rt; + bool auto_runtime_pm; int (*prepare_transfer_hardware)(struct spi_master *master); int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg); int (*unprepare_transfer_hardware)(struct spi_master *master); + /* gpio chip select */ int *cs_gpios; }; -- cgit v1.2.3-71-gd317 From 564c65049eddb1a95b48958080db97eda88c98dd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:13:55 +0200 Subject: ASoC: dapm: Move snd_soc_dapm_update from dapm context to card The update field of a DAPM context is only assigned while the card's dapm_mutex is locked, the field is also cleared again while the mutex is stil locked. So there will only ever be one DAPM context at a time with a non-NULL update field. So it is safe to move the update field from the DAPM context struct to the card struct. Doing so will allow further cleanups in this area. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 -- include/sound/soc.h | 1 + sound/soc/soc-dapm.c | 22 +++++++++++----------- 3 files changed, 12 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index e77c6f5a8390..3397292d94c8 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -575,8 +575,6 @@ struct snd_soc_dapm_context { struct delayed_work delayed_work; unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */ - struct snd_soc_dapm_update *update; - void (*seq_notifier)(struct snd_soc_dapm_context *, enum snd_soc_dapm_type, int); diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7ec15a..b1e1f967ae1e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1042,6 +1042,7 @@ struct snd_soc_card { /* Generic DAPM context for the card */ struct snd_soc_dapm_context dapm; struct snd_soc_dapm_stats dapm_stats; + struct snd_soc_dapm_update *update; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_card_root; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 366daef006ed..7449e27bf133 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1425,7 +1425,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm, static void dapm_widget_update(struct snd_soc_dapm_context *dapm) { - struct snd_soc_dapm_update *update = dapm->update; + struct snd_soc_dapm_update *update = dapm->card->update; struct snd_soc_dapm_widget_list *wlist; struct snd_soc_dapm_widget *w = NULL; unsigned int wi; @@ -1959,9 +1959,9 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - dapm->update = update; + card->update = update; ret = soc_dapm_mux_update_power(dapm, kcontrol, mux, e); - dapm->update = NULL; + card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2002,9 +2002,9 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, int ret; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - dapm->update = update; + card->update = update; ret = soc_dapm_mixer_update_power(dapm, kcontrol, connect); - dapm->update = NULL; + card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) soc_dpcm_runtime_update(card); @@ -2693,11 +2693,11 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, update.mask = mask; update.val = val; - widget->dapm->update = &update; + card->update = &update; soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect); - widget->dapm->update = NULL; + card->update = NULL; } mutex_unlock(&card->dapm_mutex); @@ -2775,11 +2775,11 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, update.reg = e->reg; update.mask = mask; update.val = val; - widget->dapm->update = &update; + card->update = &update; soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); - widget->dapm->update = NULL; + card->update = NULL; } mutex_unlock(&card->dapm_mutex); @@ -2928,11 +2928,11 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, update.reg = e->reg; update.mask = mask; update.val = val; - widget->dapm->update = &update; + card->update = &update; soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e); - widget->dapm->update = NULL; + card->update = NULL; } mutex_unlock(&card->dapm_mutex); -- cgit v1.2.3-71-gd317 From eee5d7f99ae95059e1a3d1cfa2dea3ed8dbd94ac Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:13:57 +0200 Subject: ASoC: dapm: Add a helper to get the CODEC for DAPM kcontrol We use the same 3 lines to get the CODEC for a kcontrol in a quite a few places. This patch puts them into a common helper function. Having this encapsulated in a helper function will also make it more easier to eventually change the data layout of the kcontrol's private data. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 ++ sound/soc/codecs/tlv320aic3x.c | 7 +++---- sound/soc/codecs/twl6040.c | 4 +--- sound/soc/codecs/wm8903.c | 4 +--- sound/soc/codecs/wm8994.c | 4 +--- sound/soc/codecs/wm8995.c | 5 +---- sound/soc/codecs/wm_hubs.c | 8 ++------ sound/soc/soc-dapm.c | 40 +++++++++++++++++++++++----------------- 8 files changed, 34 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3397292d94c8..ebfae8f3fda7 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -427,6 +427,8 @@ void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list); +struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol); + /* dapm widget types */ enum snd_soc_dapm_type { snd_soc_dapm_input = 0, /* input pin */ diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 1325c0c0df50..fec0db04262d 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -138,8 +138,7 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = { static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -165,14 +164,14 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, mask <<= shift; val <<= shift; - change = snd_soc_test_bits(widget->codec, val, mask, reg); + change = snd_soc_test_bits(codec, val, mask, reg); if (change) { update.kcontrol = kcontrol; update.reg = reg; update.mask = mask; update.val = val; - snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, connect, + snd_soc_dapm_mixer_update_power(&codec->dapm, kcontrol, connect, &update); } diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 44621ddc332d..d6c5bf14179a 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -437,9 +437,7 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data) static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index fa24cedee687..eebcb1da3b7b 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -364,9 +364,7 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm, static int wm8903_class_w_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec); u16 reg; int ret; diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index ba832b77c543..eee2a01f2691 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -1437,9 +1437,7 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING, static int wm8994_put_class_w(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *w = wlist->widgets[0]; - struct snd_soc_codec *codec = w->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret; ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 90a65c427541..da2899e6c401 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -549,12 +549,9 @@ static int check_clk_sys(struct snd_soc_dapm_widget *source, static int wm8995_put_class_w(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *w = wlist->widgets[0]; - struct snd_soc_codec *codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret; - codec = w->codec; ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); wm8995_update_class_w(codec); return ret; diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 2d9e099415a5..8b50e5958de5 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -699,9 +699,7 @@ EXPORT_SYMBOL_GPL(wm_hubs_update_class_w); static int class_w_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret; ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); @@ -721,9 +719,7 @@ static int class_w_put_volsw(struct snd_kcontrol *kcontrol, static int class_w_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); int ret; ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 5db8df2f8866..b18ac5b1cc2e 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -174,6 +174,17 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); } +/** + * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol + * @kcontrol: The kcontrol + */ +struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol) +{ + struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); + return wlist->widgets[0]->codec; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec); + static void dapm_reset(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; @@ -2617,8 +2628,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -2628,12 +2638,12 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, unsigned int invert = mc->invert; if (snd_soc_volsw_is_stereo(mc)) - dev_warn(widget->dapm->dev, + dev_warn(codec->dapm.dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name); ucontrol->value.integer.value[0] = - (snd_soc_read(widget->codec, reg) >> shift) & mask; + (snd_soc_read(codec, reg) >> shift) & mask; if (invert) ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; @@ -2654,9 +2664,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; @@ -2670,7 +2678,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, struct snd_soc_dapm_update update; if (snd_soc_volsw_is_stereo(mc)) - dev_warn(widget->dapm->dev, + dev_warn(codec->dapm.dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name); @@ -2684,7 +2692,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = snd_soc_test_bits(widget->codec, reg, mask, val); + change = snd_soc_test_bits(codec, reg, mask, val); if (change) { update.kcontrol = kcontrol; update.reg = reg; @@ -2715,12 +2723,11 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val; - val = snd_soc_read(widget->codec, e->reg); + val = snd_soc_read(codec, e->reg); ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & e->mask; if (e->shift_l != e->shift_r) ucontrol->value.enumerated.item[1] = @@ -2765,7 +2772,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { widget->value = val; @@ -2854,12 +2861,11 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int reg_val, val, mux; - reg_val = snd_soc_read(widget->codec, e->reg); + reg_val = snd_soc_read(codec, e->reg); val = (reg_val >> e->shift_l) & e->mask; for (mux = 0; mux < e->max; mux++) { if (val == e->values[mux]) @@ -2918,7 +2924,7 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = snd_soc_test_bits(widget->codec, e->reg, mask, val); + change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { widget->value = val; -- cgit v1.2.3-71-gd317 From cf7c1de20c576477d42deae255cbc6e439bb5dc0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:13:59 +0200 Subject: ASoC: dapm: Move 'value' field from widget to control The 'value' field is really per control and not per widget. Currently it is only used for virtual MUXes, which only have one control per widget. So in that case there is not so much of a difference between whether it is stored per widget or per control. Moving the 'value' field from the widget to the control will allow us to use it also for cases where we have more than one control per widget. E.g. for mixers with multiple input controls. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 - sound/soc/soc-dapm.c | 53 +++++++++++++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index ebfae8f3fda7..d7d26cc8e3fc 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -523,7 +523,6 @@ struct snd_soc_dapm_widget { /* dapm control */ int reg; /* negative reg = no direct dapm */ unsigned char shift; /* bits to shift */ - unsigned int value; /* widget current value */ unsigned int mask; /* non-shifted mask */ unsigned int on_val; /* on state value */ unsigned int off_val; /* off state value */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index da35b10ce6d1..bad6f6db74c9 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -175,6 +175,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( } struct dapm_kcontrol_data { + unsigned int value; struct snd_soc_dapm_widget_list wlist; }; @@ -233,6 +234,26 @@ static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, return 0; } +static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + return data->value; +} + +static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, + unsigned int value) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + if (data->value == value) + return false; + + data->value = value; + + return true; +} + /** * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol * @kcontrol: The kcontrol @@ -2786,9 +2807,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; @@ -2811,8 +2830,6 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { - widget->value = val; - update.kcontrol = kcontrol; update.reg = e->reg; update.mask = mask; @@ -2839,11 +2856,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - - ucontrol->value.enumerated.item[0] = widget->value; - + ucontrol->value.enumerated.item[0] = dapm_kcontrol_get_value(kcontrol); return 0; } EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); @@ -2858,10 +2871,9 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; + unsigned int value; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int change; @@ -2871,11 +2883,10 @@ int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); - change = widget->value != ucontrol->value.enumerated.item[0]; - if (change) { - widget->value = ucontrol->value.enumerated.item[0]; - soc_dapm_mux_update_power(card, kcontrol, widget->value, e); - } + value = ucontrol->value.enumerated.item[0]; + change = dapm_kcontrol_set_value(kcontrol, value); + if (change) + soc_dapm_mux_update_power(card, kcontrol, value, e); mutex_unlock(&card->dapm_mutex); return change; @@ -2938,9 +2949,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double); int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_dapm_widget_list *wlist = dapm_kcontrol_get_wlist(kcontrol); - struct snd_soc_dapm_widget *widget = wlist->widgets[0]; - struct snd_soc_codec *codec = widget->codec; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); struct snd_soc_card *card = codec->card; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val, mux, change; @@ -2963,8 +2972,6 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, change = snd_soc_test_bits(codec, e->reg, mask, val); if (change) { - widget->value = val; - update.kcontrol = kcontrol; update.reg = e->reg; update.mask = mask; -- cgit v1.2.3-71-gd317 From 5106b92f80a2cd37c52cffed80b4f5acfb77ccfd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:14:00 +0200 Subject: ASoC: dapm: Keep a list of paths per kcontrol Currently we store for each path which control (if any at all) is associated with that control. But we are only ever interested in the reverse relationship, i.e. we want to know all the paths a certain control is associated with. This is currently implemented by always iterating over all paths. This patch updates the code to keep a list for each control which contains all the paths that are associated with that control. This improves the run time of e.g. soc_dapm_mixer_update_power() and soc_dapm_mux_update_power() from O(n) (with n being the number of paths for the card) to O(1). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 +- sound/soc/soc-dapm.c | 40 ++++++++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index d7d26cc8e3fc..693c75bbd5d1 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -490,7 +490,6 @@ struct snd_soc_dapm_path { /* source (input) and sink (output) widgets */ struct snd_soc_dapm_widget *source; struct snd_soc_dapm_widget *sink; - struct snd_kcontrol *kcontrol; /* status */ u32 connect:1; /* source and sink widgets are connected */ @@ -503,6 +502,7 @@ struct snd_soc_dapm_path { struct list_head list_source; struct list_head list_sink; + struct list_head list_kcontrol; struct list_head list; }; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index bad6f6db74c9..b779d36d5b3a 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -176,6 +176,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( struct dapm_kcontrol_data { unsigned int value; + struct list_head paths; struct snd_soc_dapm_widget_list wlist; }; @@ -194,6 +195,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, data->wlist.widgets[0] = widget; data->wlist.num_widgets = 1; + INIT_LIST_HEAD(&data->paths); kcontrol->private_data = data; @@ -234,6 +236,26 @@ static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol, return 0; } +static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol, + struct snd_soc_dapm_path *path) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + list_add_tail(&path->list_kcontrol, &data->paths); +} + +static struct list_head *dapm_kcontrol_get_path_list( + const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + return &data->paths; +} + +#define dapm_kcontrol_for_each_path(path, kcontrol) \ + list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \ + list_kcontrol) + static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); @@ -671,7 +693,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, } w->kcontrols[kci] = kcontrol; - path->kcontrol = kcontrol; + dapm_kcontrol_add_path(kcontrol, path); return 0; } @@ -691,7 +713,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w) continue; if (w->kcontrols[i]) { - path->kcontrol = w->kcontrols[i]; + dapm_kcontrol_add_path(w->kcontrols[i], path); continue; } @@ -730,7 +752,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w) return ret; list_for_each_entry(path, &w->sources, list_sink) - path->kcontrol = w->kcontrols[0]; + dapm_kcontrol_add_path(w->kcontrols[0], path); return 0; } @@ -1990,10 +2012,7 @@ static int soc_dapm_mux_update_power(struct snd_soc_card *card, int found = 0; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &card->paths, list) { - if (path->kcontrol != kcontrol) - continue; - + dapm_kcontrol_for_each_path(path, kcontrol) { if (!path->name || !e->texts[mux]) continue; @@ -2043,11 +2062,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card, int found = 0; /* find dapm widget path assoc with kcontrol */ - list_for_each_entry(path, &card->paths, list) { - if (path->kcontrol != kcontrol) - continue; - - /* found, now check type */ + dapm_kcontrol_for_each_path(path, kcontrol) { found = 1; path->connect = connect; dapm_mark_dirty(path->source, "mixer connection"); @@ -2152,6 +2167,7 @@ static void dapm_free_path(struct snd_soc_dapm_path *path) { list_del(&path->list_sink); list_del(&path->list_source); + list_del(&path->list_kcontrol); list_del(&path->list); kfree(path); } -- cgit v1.2.3-71-gd317 From de9ba98b6d2629f53fd271a973176c2fa9736d9c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 29 Jul 2013 17:14:01 +0200 Subject: ASoC: dapm: Make widget power register settings more flexible Currently the DAPM code is limited to only setting or clearing a single bit in a register to power a widget up or down. This patch extends the DAPM code to be more flexible in that regard and allow widgets to use arbitrary values to be used to put a widget in either on or off state. Since the snd_soc_dapm_widget struct already contains a on_val and off_val field no additional fields need to be added and in fact the invert field can even be removed. Also the generated code is slightly smaller. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 163 +++++++++++++++++++++++++++-------------------- sound/soc/soc-dapm.c | 34 ++++------ 2 files changed, 106 insertions(+), 91 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 693c75bbd5d1..3575721a955d 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -70,121 +70,144 @@ struct device; .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD} +#define SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) \ + .reg = wreg, .mask = 1, .shift = wshift, \ + .on_val = winvert ? 0 : 1, .off_val = winvert ? 1 : 0 + /* path domain */ #define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\ wcontrols, wncontrols) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\ wcontrols, wncontrols) \ -{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} +{ .id = snd_soc_dapm_out_drv, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \ wcontrols, wncontrols)\ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \ wcontrols, wncontrols)\ -{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ - .num_kcontrols = wncontrols} +{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols} #define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \ -{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0} +{ .id = snd_soc_dapm_micbias, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = NULL, .num_kcontrols = 0} #define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} +{ .id = snd_soc_dapm_switch, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} +{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, \ + .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1} +{ .id = snd_soc_dapm_virt_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1} #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \ -{ .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ - .num_kcontrols = 1} +{ .id = snd_soc_dapm_value_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1} /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\ wcontrols) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} #define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \ wcontrols)\ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} #define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \ wcontrols)\ -{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \ - .num_kcontrols = ARRAY_SIZE(wcontrols)} +{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)} /* path domain with event - event handler must return 0 for success */ #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ +{ .id = snd_soc_dapm_out_drv, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \ wncontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \ wcontrols, wncontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, \ +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, \ .num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ +{ .id = snd_soc_dapm_switch, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ +{ .id = snd_soc_dapm_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \ +{ .id = snd_soc_dapm_virt_mux, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = 1, \ .event = wevent, .event_flags = wflags} /* additional sequencing control within an event type */ #define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \ wevent, wflags) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .event = wevent, .event_flags = wflags, \ +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .event = wevent, .event_flags = wflags, \ .subseq = wsubseq} #define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \ wflags) \ -{ .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .event = wevent, \ - .event_flags = wflags, .subseq = wsubseq} +{ .id = snd_soc_dapm_supply, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .event = wevent, .event_flags = wflags, .subseq = wsubseq} /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */ #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ +{ .id = snd_soc_dapm_pga, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .event = wevent, .event_flags = wflags} #define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \ wevent, wflags) \ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ .event = wevent, .event_flags = wflags} #define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \ wcontrols, wevent, wflags) \ -{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \ - .invert = winvert, .kcontrol_news = wcontrols, \ - .num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags} +{ .id = snd_soc_dapm_mixer, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \ + .event = wevent, .event_flags = wflags} /* events that are pre and post DAPM */ #define SND_SOC_DAPM_PRE(wname, wevent) \ @@ -199,35 +222,36 @@ struct device; /* stream domain */ #define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \ { .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert } + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } #define SND_SOC_DAPM_AIF_IN_E(wname, stname, wslot, wreg, wshift, winvert, \ wevent, wflags) \ { .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags } #define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \ { .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert } + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } #define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wslot, wreg, wshift, winvert, \ wevent, wflags) \ { .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \ - .reg = wreg, .shift = wshift, .invert = winvert, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags } #define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \ -{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert} +{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) } #define SND_SOC_DAPM_DAC_E(wname, stname, wreg, wshift, winvert, \ wevent, wflags) \ -{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert, \ +{ .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags} + #define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \ -{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert} +{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), } #define SND_SOC_DAPM_ADC_E(wname, stname, wreg, wshift, winvert, \ wevent, wflags) \ -{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \ - .shift = wshift, .invert = winvert, \ +{ .id = snd_soc_dapm_adc, .name = wname, .sname = stname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_CLOCK_SUPPLY(wname) \ { .id = snd_soc_dapm_clock_supply, .name = wname, \ @@ -241,14 +265,14 @@ struct device; .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} #define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \ -{ .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \ - .shift = wshift, .invert = winvert, .event = wevent, \ - .event_flags = wflags} +{ .id = snd_soc_dapm_supply, .name = wname, \ + SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \ + .event = wevent, .event_flags = wflags} #define SND_SOC_DAPM_REGULATOR_SUPPLY(wname, wdelay, wflags) \ { .id = snd_soc_dapm_regulator_supply, .name = wname, \ .reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \ .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \ - .invert = wflags} + .on_val = wflags} /* dapm kcontrol types */ @@ -527,7 +551,6 @@ struct snd_soc_dapm_widget { unsigned int on_val; /* on state value */ unsigned int off_val; /* off state value */ unsigned char power:1; /* block power status */ - unsigned char invert:1; /* invert the power bit */ unsigned char active:1; /* active stream on DAC, ADC's */ unsigned char connected:1; /* connected codec pin */ unsigned char new:1; /* cnew complete */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b779d36d5b3a..59bcc66358ca 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1122,7 +1122,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, int ret; if (SND_SOC_DAPM_EVENT_ON(event)) { - if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { + if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, false); if (ret != 0) dev_warn(w->dapm->dev, @@ -1132,7 +1132,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w, return regulator_enable(w->regulator); } else { - if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { + if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, true); if (ret != 0) dev_warn(w->dapm->dev, @@ -1360,26 +1360,21 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card, struct list_head *pending) { struct snd_soc_dapm_widget *w; - int reg, power; + int reg; unsigned int value = 0; unsigned int mask = 0; - unsigned int cur_mask; reg = list_first_entry(pending, struct snd_soc_dapm_widget, power_list)->reg; list_for_each_entry(w, pending, power_list) { - cur_mask = 1 << w->shift; BUG_ON(reg != w->reg); - if (w->invert) - power = !w->power; + mask |= w->mask << w->shift; + if (w->power) + value |= w->on_val << w->shift; else - power = w->power; - - mask |= cur_mask; - if (power) - value |= cur_mask; + value |= w->off_val << w->shift; pop_dbg(w->dapm->dev, card->pop_time, "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", @@ -1867,8 +1862,8 @@ static ssize_t dapm_widget_power_read_file(struct file *file, if (w->reg >= 0) ret += snprintf(buf + ret, PAGE_SIZE - ret, - " - R%d(0x%x) bit %d", - w->reg, w->reg, w->shift); + " - R%d(0x%x) mask 0x%x", + w->reg, w->reg, w->mask << w->shift); ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n"); @@ -2669,12 +2664,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) /* Read the initial power state from the device */ if (w->reg >= 0) { - val = soc_widget_read(w, w->reg); - val &= 1 << w->shift; - if (w->invert) - val = !val; - - if (val) + val = soc_widget_read(w, w->reg) >> w->shift; + val &= w->mask; + if (val == w->on_val) w->power = 1; } @@ -3093,7 +3085,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, return NULL; } - if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) { + if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) { ret = regulator_allow_bypass(w->regulator, true); if (ret != 0) dev_warn(w->dapm->dev, -- cgit v1.2.3-71-gd317 From 055560b04a8cd063aea916fd083b7aec02c2adb8 Mon Sep 17 00:00:00 2001 From: Elen Song Date: Mon, 22 Jul 2013 16:30:29 +0800 Subject: serial: at91: distinguish usart and uart Distinguish usart and uart by read ip name register, The usart read name is "USAR", The uart and dbgu read name is "DBGU". Signed-off-by: Elen Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/atmel_serial.h | 2 ++ 2 files changed, 39 insertions(+) (limited to 'include') diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 1db68713d656..8dbc3e67dfa4 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -97,6 +97,7 @@ static void atmel_stop_rx(struct uart_port *port); #define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR) #define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR) #define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR) +#define UART_GET_IP_NAME(port) __raw_readl((port)->membase + ATMEL_US_NAME) /* PDC registers */ #define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR) @@ -166,6 +167,7 @@ struct atmel_uart_port { struct serial_rs485 rs485; /* rs485 settings */ unsigned int tx_done_mask; + bool is_usart; /* usart or uart */ int (*prepare_rx)(struct uart_port *port); int (*prepare_tx)(struct uart_port *port); void (*schedule_rx)(struct uart_port *port); @@ -1477,6 +1479,34 @@ static void atmel_set_ops(struct uart_port *port) } } +/* + * Get ip name usart or uart + */ +static int atmel_get_ip_name(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + int name = UART_GET_IP_NAME(port); + int usart, uart; + /* usart and uart ascii */ + usart = 0x55534152; + uart = 0x44424755; + + atmel_port->is_usart = false; + + if (name == usart) { + dev_dbg(port->dev, "This is usart\n"); + atmel_port->is_usart = true; + } else if (name == uart) { + dev_dbg(port->dev, "This is uart\n"); + atmel_port->is_usart = false; + } else { + dev_err(port->dev, "Not supported ip name, set to uart\n"); + return -EINVAL; + } + + return 0; +} + /* * Perform initialization and enable port for reception */ @@ -2336,6 +2366,13 @@ static int atmel_serial_probe(struct platform_device *pdev) UART_PUT_CR(&port->uart, ATMEL_US_RTSEN); } + /* + * Get port name of usart or uart + */ + ret = atmel_get_ip_name(&port->uart); + if (ret < 0) + goto err_add_port; + return 0; err_add_port: diff --git a/include/linux/atmel_serial.h b/include/linux/atmel_serial.h index fd6833764d72..be201ca2990c 100644 --- a/include/linux/atmel_serial.h +++ b/include/linux/atmel_serial.h @@ -124,4 +124,6 @@ #define ATMEL_US_NER 0x44 /* Number of Errors Register */ #define ATMEL_US_IF 0x4c /* IrDA Filter Register */ +#define ATMEL_US_NAME 0xf0 /* Ip Name */ + #endif -- cgit v1.2.3-71-gd317 From e66c33d579ea566d10e8c8695a7168aae3e02992 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 12 Jul 2013 16:50:28 -0400 Subject: rcu: Add const annotation to char * for RCU tracepoints and functions All the RCU tracepoints and functions that reference char pointers do so with just 'char *' even though they do not modify the contents of the string itself. This will cause warnings if a const char * is used in one of these functions. The RCU tracepoints store the pointer to the string to refer back to them when the trace output is displayed. As this can be minutes, hours or even days later, those strings had better be constant. This change also opens the door to allow the RCU tracepoint strings and their addresses to be exported so that userspace tracing tools can translate the contents of the pointers of the RCU tracepoints. Signed-off-by: Steven Rostedt --- include/linux/rcupdate.h | 4 +-- include/trace/events/rcu.h | 82 +++++++++++++++++++++++----------------------- kernel/rcu.h | 2 +- kernel/rcupdate.c | 2 +- kernel/rcutiny.c | 2 +- kernel/rcutiny_plugin.h | 2 +- kernel/rcutorture.c | 8 ++--- kernel/rcutree.c | 4 +-- kernel/rcutree.h | 2 +- 9 files changed, 54 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 4b14bdc911d7..0c38abbe6e35 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -52,7 +52,7 @@ extern int rcutorture_runnable; /* for sysctl */ #if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) extern void rcutorture_record_test_transition(void); extern void rcutorture_record_progress(unsigned long vernum); -extern void do_trace_rcu_torture_read(char *rcutorturename, +extern void do_trace_rcu_torture_read(const char *rcutorturename, struct rcu_head *rhp, unsigned long secs, unsigned long c_old, @@ -65,7 +65,7 @@ static inline void rcutorture_record_progress(unsigned long vernum) { } #ifdef CONFIG_RCU_TRACE -extern void do_trace_rcu_torture_read(char *rcutorturename, +extern void do_trace_rcu_torture_read(const char *rcutorturename, struct rcu_head *rhp, unsigned long secs, unsigned long c_old, diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index 59ebcc89f148..ee2376cfaab3 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h @@ -19,12 +19,12 @@ */ TRACE_EVENT(rcu_utilization, - TP_PROTO(char *s), + TP_PROTO(const char *s), TP_ARGS(s), TP_STRUCT__entry( - __field(char *, s) + __field(const char *, s) ), TP_fast_assign( @@ -51,14 +51,14 @@ TRACE_EVENT(rcu_utilization, */ TRACE_EVENT(rcu_grace_period, - TP_PROTO(char *rcuname, unsigned long gpnum, char *gpevent), + TP_PROTO(const char *rcuname, unsigned long gpnum, const char *gpevent), TP_ARGS(rcuname, gpnum, gpevent), TP_STRUCT__entry( - __field(char *, rcuname) + __field(const char *, rcuname) __field(unsigned long, gpnum) - __field(char *, gpevent) + __field(const char *, gpevent) ), TP_fast_assign( @@ -89,21 +89,21 @@ TRACE_EVENT(rcu_grace_period, */ TRACE_EVENT(rcu_future_grace_period, - TP_PROTO(char *rcuname, unsigned long gpnum, unsigned long completed, + TP_PROTO(const char *rcuname, unsigned long gpnum, unsigned long completed, unsigned long c, u8 level, int grplo, int grphi, - char *gpevent), + const char *gpevent), TP_ARGS(rcuname, gpnum, completed, c, level, grplo, grphi, gpevent), TP_STRUCT__entry( - __field(char *, rcuname) + __field(const char *, rcuname) __field(unsigned long, gpnum) __field(unsigned long, completed) __field(unsigned long, c) __field(u8, level) __field(int, grplo) __field(int, grphi) - __field(char *, gpevent) + __field(const char *, gpevent) ), TP_fast_assign( @@ -132,13 +132,13 @@ TRACE_EVENT(rcu_future_grace_period, */ TRACE_EVENT(rcu_grace_period_init, - TP_PROTO(char *rcuname, unsigned long gpnum, u8 level, + TP_PROTO(const char *rcuname, unsigned long gpnum, u8 level, int grplo, int grphi, unsigned long qsmask), TP_ARGS(rcuname, gpnum, level, grplo, grphi, qsmask), TP_STRUCT__entry( - __field(char *, rcuname) + __field(const char *, rcuname) __field(unsigned long, gpnum) __field(u8, level) __field(int, grplo) @@ -168,12 +168,12 @@ TRACE_EVENT(rcu_grace_period_init, */ TRACE_EVENT(rcu_preempt_task, - TP_PROTO(char *rcuname, int pid, unsigned long gpnum), + TP_PROTO(const char *rcuname, int pid, unsigned long gpnum), TP_ARGS(rcuname, pid, gpnum), TP_STRUCT__entry( - __field(char *, rcuname) + __field(const char *, rcuname) __field(unsigned long, gpnum) __field(int, pid) ), @@ -195,12 +195,12 @@ TRACE_EVENT(rcu_preempt_task, */ TRACE_EVENT(rcu_unlock_preempted_task, - TP_PROTO(char *rcuname, unsigned long gpnum, int pid), + TP_PROTO(const char *rcuname, unsigned long gpnum, int pid), TP_ARGS(rcuname, gpnum, pid), TP_STRUCT__entry( - __field(char *, rcuname) + __field(const char *, rcuname) __field(unsigned long, gpnum) __field(int, pid) ), @@ -224,14 +224,14 @@ TRACE_EVENT(rcu_unlock_preempted_task, */ TRACE_EVENT(rcu_quiescent_state_report, - TP_PROTO(char *rcuname, unsigned long gpnum, + TP_PROTO(const char *rcuname, unsigned long gpnum, unsigned long mask, unsigned long qsmask, u8 level, int grplo, int grphi, int gp_tasks), TP_ARGS(rcuname, gpnum, mask, qsmask, level, grplo, grphi, gp_tasks), TP_STRUCT__entry( - __field(char *, rcuname) + __field(const char *, rcuname) __field(unsigned long, gpnum) __field(unsigned long, mask) __field(unsigned long, qsmask) @@ -268,15 +268,15 @@ TRACE_EVENT(rcu_quiescent_state_report, */ TRACE_EVENT(rcu_fqs, - TP_PROTO(char *rcuname, unsigned long gpnum, int cpu, char *qsevent), + TP_PROTO(const char *rcuname, unsigned long gpnum, int cpu, const char *qsevent), TP_ARGS(rcuname, gpnum, cpu, qsevent), TP_STRUCT__entry( - __field(char *, rcuname) + __field(const char *, rcuname) __field(unsigned long, gpnum) __field(int, cpu) - __field(char *, qsevent) + __field(const char *, qsevent) ), TP_fast_assign( @@ -308,12 +308,12 @@ TRACE_EVENT(rcu_fqs, */ TRACE_EVENT(rcu_dyntick, - TP_PROTO(char *polarity, long long oldnesting, long long newnesting), + TP_PROTO(const char *polarity, long long oldnesting, long long newnesting), TP_ARGS(polarity, oldnesting, newnesting), TP_STRUCT__entry( - __field(char *, polarity) + __field(const char *, polarity) __field(long long, oldnesting) __field(long long, newnesting) ), @@ -352,12 +352,12 @@ TRACE_EVENT(rcu_dyntick, */ TRACE_EVENT(rcu_prep_idle, - TP_PROTO(char *reason), + TP_PROTO(const char *reason), TP_ARGS(reason), TP_STRUCT__entry( - __field(char *, reason) + __field(const char *, reason) ), TP_fast_assign( @@ -376,13 +376,13 @@ TRACE_EVENT(rcu_prep_idle, */ TRACE_EVENT(rcu_callback, - TP_PROTO(char *rcuname, struct rcu_head *rhp, long qlen_lazy, + TP_PROTO(const char *rcuname, struct rcu_head *rhp, long qlen_lazy, long qlen), TP_ARGS(rcuname, rhp, qlen_lazy, qlen), TP_STRUCT__entry( - __field(char *, rcuname) + __field(const char *, rcuname) __field(void *, rhp) __field(void *, func) __field(long, qlen_lazy) @@ -412,13 +412,13 @@ TRACE_EVENT(rcu_callback, */ TRACE_EVENT(rcu_kfree_callback, - TP_PROTO(char *rcuname, struct rcu_head *rhp, unsigned long offset, + TP_PROTO(const char *rcuname, struct rcu_head *rhp, unsigned long offset, long qlen_lazy, long qlen), TP_ARGS(rcuname, rhp, offset, qlen_lazy, qlen), TP_STRUCT__entry( - __field(char *, rcuname) + __field(const char *, rcuname) __field(void *, rhp) __field(unsigned long, offset) __field(long, qlen_lazy) @@ -447,12 +447,12 @@ TRACE_EVENT(rcu_kfree_callback, */ TRACE_EVENT(rcu_batch_start, - TP_PROTO(char *rcuname, long qlen_lazy, long qlen, long blimit), + TP_PROTO(const char *rcuname, long qlen_lazy, long qlen, long blimit), TP_ARGS(rcuname, qlen_lazy, qlen, blimit), TP_STRUCT__entry( - __field(char *, rcuname) + __field(const char *, rcuname) __field(long, qlen_lazy) __field(long, qlen) __field(long, blimit) @@ -477,12 +477,12 @@ TRACE_EVENT(rcu_batch_start, */ TRACE_EVENT(rcu_invoke_callback, - TP_PROTO(char *rcuname, struct rcu_head *rhp), + TP_PROTO(const char *rcuname, struct rcu_head *rhp), TP_ARGS(rcuname, rhp), TP_STRUCT__entry( - __field(char *, rcuname) + __field(const char *, rcuname) __field(void *, rhp) __field(void *, func) ), @@ -506,12 +506,12 @@ TRACE_EVENT(rcu_invoke_callback, */ TRACE_EVENT(rcu_invoke_kfree_callback, - TP_PROTO(char *rcuname, struct rcu_head *rhp, unsigned long offset), + TP_PROTO(const char *rcuname, struct rcu_head *rhp, unsigned long offset), TP_ARGS(rcuname, rhp, offset), TP_STRUCT__entry( - __field(char *, rcuname) + __field(const char *, rcuname) __field(void *, rhp) __field(unsigned long, offset) ), @@ -539,13 +539,13 @@ TRACE_EVENT(rcu_invoke_kfree_callback, */ TRACE_EVENT(rcu_batch_end, - TP_PROTO(char *rcuname, int callbacks_invoked, + TP_PROTO(const char *rcuname, int callbacks_invoked, bool cb, bool nr, bool iit, bool risk), TP_ARGS(rcuname, callbacks_invoked, cb, nr, iit, risk), TP_STRUCT__entry( - __field(char *, rcuname) + __field(const char *, rcuname) __field(int, callbacks_invoked) __field(bool, cb) __field(bool, nr) @@ -577,13 +577,13 @@ TRACE_EVENT(rcu_batch_end, */ TRACE_EVENT(rcu_torture_read, - TP_PROTO(char *rcutorturename, struct rcu_head *rhp, + TP_PROTO(const char *rcutorturename, struct rcu_head *rhp, unsigned long secs, unsigned long c_old, unsigned long c), TP_ARGS(rcutorturename, rhp, secs, c_old, c), TP_STRUCT__entry( - __field(char *, rcutorturename) + __field(const char *, rcutorturename) __field(struct rcu_head *, rhp) __field(unsigned long, secs) __field(unsigned long, c_old) @@ -623,13 +623,13 @@ TRACE_EVENT(rcu_torture_read, */ TRACE_EVENT(rcu_barrier, - TP_PROTO(char *rcuname, char *s, int cpu, int cnt, unsigned long done), + TP_PROTO(const char *rcuname, const char *s, int cpu, int cnt, unsigned long done), TP_ARGS(rcuname, s, cpu, cnt, done), TP_STRUCT__entry( - __field(char *, rcuname) - __field(char *, s) + __field(const char *, rcuname) + __field(const char *, s) __field(int, cpu) __field(int, cnt) __field(unsigned long, done) diff --git a/kernel/rcu.h b/kernel/rcu.h index 7f8e7590e3e5..0a90ccc65bfb 100644 --- a/kernel/rcu.h +++ b/kernel/rcu.h @@ -94,7 +94,7 @@ static inline void debug_rcu_head_unqueue(struct rcu_head *head) extern void kfree(const void *); -static inline bool __rcu_reclaim(char *rn, struct rcu_head *head) +static inline bool __rcu_reclaim(const char *rn, struct rcu_head *head) { unsigned long offset = (unsigned long)head->func; diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index cce6ba8bbace..14994d4e1a54 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -377,7 +377,7 @@ EXPORT_SYMBOL_GPL(rcuhead_debug_descr); #endif /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */ #if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) || defined(CONFIG_RCU_TRACE) -void do_trace_rcu_torture_read(char *rcutorturename, struct rcu_head *rhp, +void do_trace_rcu_torture_read(const char *rcutorturename, struct rcu_head *rhp, unsigned long secs, unsigned long c_old, unsigned long c) { diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c index aa344111de3e..9ed6075dc562 100644 --- a/kernel/rcutiny.c +++ b/kernel/rcutiny.c @@ -264,7 +264,7 @@ void rcu_check_callbacks(int cpu, int user) */ static void __rcu_process_callbacks(struct rcu_ctrlblk *rcp) { - char *rn = NULL; + const char *rn = NULL; struct rcu_head *next, *list; unsigned long flags; RCU_TRACE(int cb_count = 0); diff --git a/kernel/rcutiny_plugin.h b/kernel/rcutiny_plugin.h index 0cd385acccfa..280d06cae352 100644 --- a/kernel/rcutiny_plugin.h +++ b/kernel/rcutiny_plugin.h @@ -36,7 +36,7 @@ struct rcu_ctrlblk { RCU_TRACE(unsigned long gp_start); /* Start time for stalls. */ RCU_TRACE(unsigned long ticks_this_gp); /* Statistic for stalls. */ RCU_TRACE(unsigned long jiffies_stall); /* Jiffies at next stall. */ - RCU_TRACE(char *name); /* Name of RCU type. */ + RCU_TRACE(const char *name); /* Name of RCU type. */ }; /* Definition for rcupdate control block. */ diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index f4871e52c546..3d936f0fbcd8 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c @@ -267,7 +267,7 @@ rcutorture_shutdown_notify(struct notifier_block *unused1, * Absorb kthreads into a kernel function that won't return, so that * they won't ever access module text or data again. */ -static void rcutorture_shutdown_absorb(char *title) +static void rcutorture_shutdown_absorb(const char *title) { if (ACCESS_ONCE(fullstop) == FULLSTOP_SHUTDOWN) { pr_notice( @@ -337,7 +337,7 @@ rcu_random(struct rcu_random_state *rrsp) } static void -rcu_stutter_wait(char *title) +rcu_stutter_wait(const char *title) { while (stutter_pause_test || !rcutorture_runnable) { if (rcutorture_runnable) @@ -366,7 +366,7 @@ struct rcu_torture_ops { int (*stats)(char *page); int irq_capable; int can_boost; - char *name; + const char *name; }; static struct rcu_torture_ops *cur_ops; @@ -1364,7 +1364,7 @@ rcu_torture_stutter(void *arg) } static inline void -rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, char *tag) +rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag) { pr_alert("%s" TORTURE_FLAG "--- %s: nreaders=%d nfakewriters=%d " diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 068de3a93606..30201494560b 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -1032,7 +1032,7 @@ static unsigned long rcu_cbs_completed(struct rcu_state *rsp, * rcu_nocb_wait_gp(). */ static void trace_rcu_future_gp(struct rcu_node *rnp, struct rcu_data *rdp, - unsigned long c, char *s) + unsigned long c, const char *s) { trace_rcu_future_grace_period(rdp->rsp->name, rnp->gpnum, rnp->completed, c, rnp->level, @@ -2720,7 +2720,7 @@ static int rcu_cpu_has_callbacks(int cpu, bool *all_lazy) * Helper function for _rcu_barrier() tracing. If tracing is disabled, * the compiler is expected to optimize this away. */ -static void _rcu_barrier_trace(struct rcu_state *rsp, char *s, +static void _rcu_barrier_trace(struct rcu_state *rsp, const char *s, int cpu, unsigned long done) { trace_rcu_barrier(rsp->name, s, cpu, diff --git a/kernel/rcutree.h b/kernel/rcutree.h index b3832581043c..cbdeac6cea9e 100644 --- a/kernel/rcutree.h +++ b/kernel/rcutree.h @@ -445,7 +445,7 @@ struct rcu_state { /* for CPU stalls. */ unsigned long gp_max; /* Maximum GP duration in */ /* jiffies. */ - char *name; /* Name of structure. */ + const char *name; /* Name of structure. */ char abbr; /* Abbreviated name. */ struct list_head flavors; /* List of RCU flavors. */ struct irq_work wakeup_work; /* Postponed wakeups */ -- cgit v1.2.3-71-gd317 From 9cf7b244187bba5964d4bf7438946baa2a974466 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 30 Jul 2013 09:09:15 +0300 Subject: usb: of: fix build breakage caused by recent patches commit 052a11d (usb: phy: make PHY driver selection possible by controller drivers) changed the rules on how drivers/usb/phy/of.c would be compiled and failed to update include/linux/usb/of.h accordingly. Because of that, we can fall into situations where of_usb_get_phy_mode() is redefined. In order to fix the error, we update the IS_ENABLED() check in include/linux/usb/of.h to reflect the condition where of.c is built. Reported-by: Stephen Rothwell Signed-off-by: Felipe Balbi --- include/linux/usb/of.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index 5a7cb9ebf0ef..8c38aa26b3bb 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -27,7 +27,7 @@ of_usb_get_maximum_speed(struct device_node *np) } #endif -#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_USB_PHY) +#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_USB_SUPPORT) enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np); #else static inline enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np) -- cgit v1.2.3-71-gd317 From a24a4b9f3f7a4cfba3af0e8079e25972b88a5afa Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Tue, 30 Jul 2013 15:30:05 +0800 Subject: include/linux/coda.h: remove useless '#else' '#else' is useless, need remove. Signed-off-by: Chen Gang Signed-off-by: Jiri Kosina --- include/linux/coda.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/coda.h b/include/linux/coda.h index cff544f81105..d30209b9cef8 100644 --- a/include/linux/coda.h +++ b/include/linux/coda.h @@ -60,7 +60,6 @@ Mellon the rights to redistribute these changes without encumbrance. #if defined(__linux__) typedef unsigned long long u_quad_t; -#else #endif #include #endif -- cgit v1.2.3-71-gd317 From 1207637304990374231fe4e9aeb527904f4ec1e6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 30 Jul 2013 08:30:16 -0400 Subject: workqueue: mark WQ_NON_REENTRANT deprecated dbf2576e37 ("workqueue: make all workqueues non-reentrant") made WQ_NON_REENTRANT no-op but the following patches didn't remove the flag or update the documentation. Let's mark the flag deprecated and update the documentation accordingly. Signed-off-by: Tejun Heo --- Documentation/workqueue.txt | 18 ++++++------------ include/linux/workqueue.h | 7 ++++++- 2 files changed, 12 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/Documentation/workqueue.txt b/Documentation/workqueue.txt index a6ab4b62d926..67113f622880 100644 --- a/Documentation/workqueue.txt +++ b/Documentation/workqueue.txt @@ -100,8 +100,8 @@ Subsystems and drivers can create and queue work items through special workqueue API functions as they see fit. They can influence some aspects of the way the work items are executed by setting flags on the workqueue they are putting the work item on. These flags include -things like CPU locality, reentrancy, concurrency limits, priority and -more. To get a detailed overview refer to the API description of +things like CPU locality, concurrency limits, priority and more. To +get a detailed overview refer to the API description of alloc_workqueue() below. When a work item is queued to a workqueue, the target gcwq and @@ -166,16 +166,6 @@ resources, scheduled and executed. @flags: - WQ_NON_REENTRANT - - By default, a wq guarantees non-reentrance only on the same - CPU. A work item may not be executed concurrently on the same - CPU by multiple workers but is allowed to be executed - concurrently on multiple CPUs. This flag makes sure - non-reentrance is enforced across all CPUs. Work items queued - to a non-reentrant wq are guaranteed to be executed by at most - one worker system-wide at any given time. - WQ_UNBOUND Work items queued to an unbound wq are served by a special @@ -233,6 +223,10 @@ resources, scheduled and executed. This flag is meaningless for unbound wq. +Note that the flag WQ_NON_REENTRANT no longer exists as all workqueues +are now non-reentrant - any work item is guaranteed to be executed by +at most one worker system-wide at any given time. + @max_active: @max_active determines the maximum number of execution contexts per diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index a0ed78ab54d7..594521ba0d43 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -295,7 +295,12 @@ static inline unsigned int work_static(struct work_struct *work) { return 0; } * Documentation/workqueue.txt. */ enum { - WQ_NON_REENTRANT = 1 << 0, /* guarantee non-reentrance */ + /* + * All wqs are now non-reentrant making the following flag + * meaningless. Will be removed. + */ + WQ_NON_REENTRANT = 1 << 0, /* DEPRECATED */ + WQ_UNBOUND = 1 << 1, /* not bound to any cpu */ WQ_FREEZABLE = 1 << 2, /* freeze during suspend */ WQ_MEM_RECLAIM = 1 << 3, /* may be used for memory reclaim */ -- cgit v1.2.3-71-gd317 From 8ad928d52e63a9b7d69f0873d7318c4561e2f8cd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 30 Jul 2013 14:36:20 +0200 Subject: ACPI / PM: Use ACPI_STATE_D3_COLD instead of ACPI_STATE_D3 everywhere There are several places in the tree where ACPI_STATE_D3 is used instead of ACPI_STATE_D3_COLD which should be used instead for clarity. Modify them all to use ACPI_STATE_D3_COLD as appropriate. [The definition of ACPI_STATE_D3 itself cannot go away at this point as it is part of ACPICA.] Signed-off-by: Rafael J. Wysocki Reviewed-by: Aaron Lu --- drivers/acpi/fan.c | 4 ++-- drivers/acpi/power.c | 2 +- drivers/acpi/scan.c | 4 ++-- drivers/ata/libata-acpi.c | 4 ++-- drivers/ide/ide-acpi.c | 5 +++-- drivers/pnp/pnpacpi/core.c | 6 +++--- include/acpi/acpi_bus.h | 3 ++- 7 files changed, 15 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 5b02a0aa540c..41ade6570bc0 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -93,7 +93,7 @@ static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long if (result) return result; - *state = (acpi_state == ACPI_STATE_D3 ? 0 : + *state = (acpi_state == ACPI_STATE_D3_COLD ? 0 : (acpi_state == ACPI_STATE_D0 ? 1 : -1)); return 0; } @@ -108,7 +108,7 @@ fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) return -EINVAL; result = acpi_bus_set_power(device->handle, - state ? ACPI_STATE_D0 : ACPI_STATE_D3); + state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); return result; } diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 5c28c894c0fc..4cdcc0cf0c56 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -786,7 +786,7 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state) } } - *state = ACPI_STATE_D3; + *state = ACPI_STATE_D3_COLD; return 0; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8a46c924effd..c30df3ad2d71 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1450,8 +1450,8 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) /* Set defaults for D0 and D3 states (always valid) */ device->power.states[ACPI_STATE_D0].flags.valid = 1; device->power.states[ACPI_STATE_D0].power = 100; - device->power.states[ACPI_STATE_D3].flags.valid = 1; - device->power.states[ACPI_STATE_D3].power = 0; + device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1; + device->power.states[ACPI_STATE_D3_COLD].power = 0; /* Set D3cold's explicit_set flag if _PS3 exists. */ if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set) diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index cf4e7020adac..da8170dfc90f 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -947,11 +947,11 @@ static void pata_acpi_set_state(struct ata_port *ap, pm_message_t state) continue; acpi_bus_set_power(dev_handle, state.event & PM_EVENT_RESUME ? - ACPI_STATE_D0 : ACPI_STATE_D3); + ACPI_STATE_D0 : ACPI_STATE_D3_COLD); } if (!(state.event & PM_EVENT_RESUME)) - acpi_bus_set_power(port_handle, ACPI_STATE_D3); + acpi_bus_set_power(port_handle, ACPI_STATE_D3_COLD); } /** diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c index f1a6796b165c..140c8ef50529 100644 --- a/drivers/ide/ide-acpi.c +++ b/drivers/ide/ide-acpi.c @@ -520,11 +520,12 @@ void ide_acpi_set_state(ide_hwif_t *hwif, int on) ide_port_for_each_present_dev(i, drive, hwif) { if (drive->acpidata->obj_handle) acpi_bus_set_power(drive->acpidata->obj_handle, - on ? ACPI_STATE_D0 : ACPI_STATE_D3); + on ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); } if (!on) - acpi_bus_set_power(hwif->acpidata->obj_handle, ACPI_STATE_D3); + acpi_bus_set_power(hwif->acpidata->obj_handle, + ACPI_STATE_D3_COLD); } /** diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index 55cd459a3908..34049b0b4c73 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -131,7 +131,7 @@ static int pnpacpi_disable_resources(struct pnp_dev *dev) /* acpi_unregister_gsi(pnp_irq(dev, 0)); */ ret = 0; if (acpi_bus_power_manageable(handle)) - acpi_bus_set_power(handle, ACPI_STATE_D3); + acpi_bus_set_power(handle, ACPI_STATE_D3_COLD); /* continue even if acpi_bus_set_power() fails */ if (ACPI_FAILURE(acpi_evaluate_object(handle, "_DIS", NULL, NULL))) ret = -ENODEV; @@ -174,10 +174,10 @@ static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state) if (acpi_bus_power_manageable(handle)) { int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL, - ACPI_STATE_D3); + ACPI_STATE_D3_COLD); if (power_state < 0) power_state = (state.event == PM_EVENT_ON) ? - ACPI_STATE_D0 : ACPI_STATE_D3; + ACPI_STATE_D0 : ACPI_STATE_D3_COLD; /* * acpi_bus_set_power() often fails (keyboard port can't be diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 56e6b68c8d2f..916b18933879 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -478,7 +478,8 @@ static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) if (p) *p = ACPI_STATE_D0; - return (m >= ACPI_STATE_D0 && m <= ACPI_STATE_D3) ? m : ACPI_STATE_D0; + return (m >= ACPI_STATE_D0 && m <= ACPI_STATE_D3_COLD) ? + m : ACPI_STATE_D0; } static inline void acpi_dev_pm_add_dependent(acpi_handle handle, struct device *depdev) {} -- cgit v1.2.3-71-gd317 From bec68faaf3ba74ed0dcd5dc3a881b30aec542973 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Mon, 13 May 2013 14:45:08 -0700 Subject: aio: io_cancel() no longer returns the io_event Originally, io_event() was documented to return the io_event if cancellation succeeded - the io_event wouldn't be delivered via the ring buffer like it normally would. But this isn't what the implementation was actually doing; the only driver implementing cancellation, the usb gadget code, never returned an io_event in its cancel function. And aio_complete() was recently changed to no longer suppress event delivery if the kiocb had been cancelled. This gets rid of the unused io_event argument to kiocb_cancel() and kiocb->ki_cancel(), and changes io_cancel() to return -EINPROGRESS if kiocb->ki_cancel() returned success. Also tweak the refcounting in kiocb_cancel() to make more sense. Signed-off-by: Kent Overstreet Cc: Zach Brown Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: Mark Fasheh Cc: Joel Becker Cc: Rusty Russell Cc: Jens Axboe Cc: Asai Thambi S P Cc: Selvan Mani Cc: Sam Bradshaw Cc: Jeff Moyer Cc: Al Viro Cc: Benjamin LaHaise Signed-off-by: Benjamin LaHaise --- drivers/usb/gadget/inode.c | 3 +-- fs/aio.c | 40 ++++++++++------------------------------ include/linux/aio.h | 2 +- 3 files changed, 12 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 570c005062ab..e02c1e04529e 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -524,7 +524,7 @@ struct kiocb_priv { unsigned actual; }; -static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e) +static int ep_aio_cancel(struct kiocb *iocb) { struct kiocb_priv *priv = iocb->private; struct ep_data *epdata; @@ -540,7 +540,6 @@ static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e) // spin_unlock(&epdata->dev->lock); local_irq_enable(); - aio_put_req(iocb); return value; } diff --git a/fs/aio.c b/fs/aio.c index 7b470bfbf891..12b37689dd2c 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -358,8 +358,7 @@ void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel) } EXPORT_SYMBOL(kiocb_set_cancel_fn); -static int kiocb_cancel(struct kioctx *ctx, struct kiocb *kiocb, - struct io_event *res) +static int kiocb_cancel(struct kioctx *ctx, struct kiocb *kiocb) { kiocb_cancel_fn *old, *cancel; int ret = -EINVAL; @@ -381,12 +380,10 @@ static int kiocb_cancel(struct kioctx *ctx, struct kiocb *kiocb, atomic_inc(&kiocb->ki_users); spin_unlock_irq(&ctx->ctx_lock); - memset(res, 0, sizeof(*res)); - res->obj = (u64)(unsigned long)kiocb->ki_obj.user; - res->data = kiocb->ki_user_data; - ret = cancel(kiocb, res); + ret = cancel(kiocb); spin_lock_irq(&ctx->ctx_lock); + aio_put_req(kiocb); return ret; } @@ -408,7 +405,6 @@ static void free_ioctx(struct work_struct *work) { struct kioctx *ctx = container_of(work, struct kioctx, free_work); struct aio_ring *ring; - struct io_event res; struct kiocb *req; unsigned cpu, head, avail; @@ -419,7 +415,7 @@ static void free_ioctx(struct work_struct *work) struct kiocb, ki_list); list_del_init(&req->ki_list); - kiocb_cancel(ctx, req, &res); + kiocb_cancel(ctx, req); } spin_unlock_irq(&ctx->ctx_lock); @@ -795,21 +791,6 @@ void aio_complete(struct kiocb *iocb, long res, long res2) spin_unlock_irqrestore(&ctx->ctx_lock, flags); } - /* - * cancelled requests don't get events, userland was given one - * when the event got cancelled. - */ - if (unlikely(xchg(&iocb->ki_cancel, - KIOCB_CANCELLED) == KIOCB_CANCELLED)) { - /* - * Can't use the percpu reqs_available here - could race with - * free_ioctx() - */ - atomic_inc(&ctx->reqs_available); - /* Still need the wake_up in case free_ioctx is waiting */ - goto put_rq; - } - /* * Add a completion event to the ring buffer. Must be done holding * ctx->completion_lock to prevent other code from messing with the tail @@ -862,7 +843,6 @@ void aio_complete(struct kiocb *iocb, long res, long res2) if (iocb->ki_eventfd != NULL) eventfd_signal(iocb->ki_eventfd, 1); -put_rq: /* everything turned out well, dispose of the aiocb. */ aio_put_req(iocb); @@ -1439,7 +1419,6 @@ static struct kiocb *lookup_kiocb(struct kioctx *ctx, struct iocb __user *iocb, SYSCALL_DEFINE3(io_cancel, aio_context_t, ctx_id, struct iocb __user *, iocb, struct io_event __user *, result) { - struct io_event res; struct kioctx *ctx; struct kiocb *kiocb; u32 key; @@ -1457,18 +1436,19 @@ SYSCALL_DEFINE3(io_cancel, aio_context_t, ctx_id, struct iocb __user *, iocb, kiocb = lookup_kiocb(ctx, iocb, key); if (kiocb) - ret = kiocb_cancel(ctx, kiocb, &res); + ret = kiocb_cancel(ctx, kiocb); else ret = -EINVAL; spin_unlock_irq(&ctx->ctx_lock); if (!ret) { - /* Cancellation succeeded -- copy the result - * into the user's buffer. + /* + * The result argument is no longer used - the io_event is + * always delivered via the ring buffer. -EINPROGRESS indicates + * cancellation is progress: */ - if (copy_to_user(result, &res, sizeof(res))) - ret = -EFAULT; + ret = -EINPROGRESS; } percpu_ref_put(&ctx->users); diff --git a/include/linux/aio.h b/include/linux/aio.h index 1bdf965339f9..8c8dd1d84d2e 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -27,7 +27,7 @@ struct kiocb; */ #define KIOCB_CANCELLED ((void *) (~0ULL)) -typedef int (kiocb_cancel_fn)(struct kiocb *, struct io_event *); +typedef int (kiocb_cancel_fn)(struct kiocb *); struct kiocb { atomic_t ki_users; -- cgit v1.2.3-71-gd317 From 73a7075e3f6ec63dc359064eea6fd84f406cf2a5 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Thu, 9 May 2013 15:03:42 -0700 Subject: aio: Kill aio_rw_vect_retry() This code doesn't serve any purpose anymore, since the aio retry infrastructure has been removed. This change should be safe because aio_read/write are also used for synchronous IO, and called from do_sync_read()/do_sync_write() - and there's no looping done in the sync case (the read and write syscalls). Signed-off-by: Kent Overstreet Cc: Zach Brown Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: Mark Fasheh Cc: Joel Becker Cc: Rusty Russell Cc: Jens Axboe Cc: Asai Thambi S P Cc: Selvan Mani Cc: Sam Bradshaw Cc: Jeff Moyer Cc: Al Viro Cc: Benjamin LaHaise Signed-off-by: Benjamin LaHaise --- drivers/staging/android/logger.c | 2 +- drivers/usb/gadget/inode.c | 6 +-- fs/aio.c | 91 ++++++++-------------------------------- fs/block_dev.c | 2 +- fs/nfs/direct.c | 1 - fs/ocfs2/file.c | 6 +-- fs/read_write.c | 3 -- fs/udf/file.c | 2 +- include/linux/aio.h | 2 - mm/page_io.c | 1 - net/socket.c | 2 +- 11 files changed, 28 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c index 080abf2faf97..d72b47195ecf 100644 --- a/drivers/staging/android/logger.c +++ b/drivers/staging/android/logger.c @@ -481,7 +481,7 @@ static ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov, header.sec = now.tv_sec; header.nsec = now.tv_nsec; header.euid = current_euid(); - header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD); + header.len = min_t(size_t, iocb->ki_nbytes, LOGGER_ENTRY_MAX_PAYLOAD); header.hdr_size = sizeof(struct logger_entry); /* null writes succeed, return zero */ diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index e02c1e04529e..f255ad7f4c74 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -708,11 +708,11 @@ ep_aio_read(struct kiocb *iocb, const struct iovec *iov, if (unlikely(usb_endpoint_dir_in(&epdata->desc))) return -EINVAL; - buf = kmalloc(iocb->ki_left, GFP_KERNEL); + buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL); if (unlikely(!buf)) return -ENOMEM; - return ep_aio_rwtail(iocb, buf, iocb->ki_left, epdata, iov, nr_segs); + return ep_aio_rwtail(iocb, buf, iocb->ki_nbytes, epdata, iov, nr_segs); } static ssize_t @@ -727,7 +727,7 @@ ep_aio_write(struct kiocb *iocb, const struct iovec *iov, if (unlikely(!usb_endpoint_dir_in(&epdata->desc))) return -EINVAL; - buf = kmalloc(iocb->ki_left, GFP_KERNEL); + buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL); if (unlikely(!buf)) return -ENOMEM; diff --git a/fs/aio.c b/fs/aio.c index 57b02791d04e..d00904db69b7 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -707,7 +707,7 @@ static inline struct kiocb *aio_get_req(struct kioctx *ctx) if (unlikely(!req)) goto out_put; - atomic_set(&req->ki_users, 2); + atomic_set(&req->ki_users, 1); req->ki_ctx = ctx; return req; out_put: @@ -1051,75 +1051,9 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx) return -EINVAL; } -static void aio_advance_iovec(struct kiocb *iocb, ssize_t ret) -{ - struct iovec *iov = &iocb->ki_iovec[iocb->ki_cur_seg]; - - BUG_ON(ret <= 0); - - while (iocb->ki_cur_seg < iocb->ki_nr_segs && ret > 0) { - ssize_t this = min((ssize_t)iov->iov_len, ret); - iov->iov_base += this; - iov->iov_len -= this; - iocb->ki_left -= this; - ret -= this; - if (iov->iov_len == 0) { - iocb->ki_cur_seg++; - iov++; - } - } - - /* the caller should not have done more io than what fit in - * the remaining iovecs */ - BUG_ON(ret > 0 && iocb->ki_left == 0); -} - typedef ssize_t (aio_rw_op)(struct kiocb *, const struct iovec *, unsigned long, loff_t); -static ssize_t aio_rw_vect_retry(struct kiocb *iocb, int rw, aio_rw_op *rw_op) -{ - struct file *file = iocb->ki_filp; - struct address_space *mapping = file->f_mapping; - struct inode *inode = mapping->host; - ssize_t ret = 0; - - /* This matches the pread()/pwrite() logic */ - if (iocb->ki_pos < 0) - return -EINVAL; - - if (rw == WRITE) - file_start_write(file); - do { - ret = rw_op(iocb, &iocb->ki_iovec[iocb->ki_cur_seg], - iocb->ki_nr_segs - iocb->ki_cur_seg, - iocb->ki_pos); - if (ret > 0) - aio_advance_iovec(iocb, ret); - - /* retry all partial writes. retry partial reads as long as its a - * regular file. */ - } while (ret > 0 && iocb->ki_left > 0 && - (rw == WRITE || - (!S_ISFIFO(inode->i_mode) && !S_ISSOCK(inode->i_mode)))); - if (rw == WRITE) - file_end_write(file); - - /* This means we must have transferred all that we could */ - /* No need to retry anymore */ - if ((ret == 0) || (iocb->ki_left == 0)) - ret = iocb->ki_nbytes - iocb->ki_left; - - /* If we managed to write some out we return that, rather than - * the eventual error. */ - if (rw == WRITE - && ret < 0 && ret != -EIOCBQUEUED - && iocb->ki_nbytes - iocb->ki_left) - ret = iocb->ki_nbytes - iocb->ki_left; - - return ret; -} - static ssize_t aio_setup_vectored_rw(int rw, struct kiocb *kiocb, bool compat) { ssize_t ret; @@ -1204,9 +1138,22 @@ rw_common: return ret; req->ki_nbytes = ret; - req->ki_left = ret; - ret = aio_rw_vect_retry(req, rw, rw_op); + /* XXX: move/kill - rw_verify_area()? */ + /* This matches the pread()/pwrite() logic */ + if (req->ki_pos < 0) { + ret = -EINVAL; + break; + } + + if (rw == WRITE) + file_start_write(file); + + ret = rw_op(req, req->ki_iovec, + req->ki_nr_segs, req->ki_pos); + + if (rw == WRITE) + file_end_write(file); break; case IOCB_CMD_FDSYNC: @@ -1301,19 +1248,17 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, req->ki_pos = iocb->aio_offset; req->ki_buf = (char __user *)(unsigned long)iocb->aio_buf; - req->ki_left = req->ki_nbytes = iocb->aio_nbytes; + req->ki_nbytes = iocb->aio_nbytes; req->ki_opcode = iocb->aio_lio_opcode; ret = aio_run_iocb(req, compat); if (ret) goto out_put_req; - aio_put_req(req); /* drop extra ref to req */ return 0; out_put_req: put_reqs_available(ctx, 1); - aio_put_req(req); /* drop extra ref to req */ - aio_put_req(req); /* drop i/o ref to req */ + aio_put_req(req); return ret; } diff --git a/fs/block_dev.c b/fs/block_dev.c index c7bda5cd3da7..8772b155cb30 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1542,7 +1542,7 @@ static ssize_t blkdev_aio_read(struct kiocb *iocb, const struct iovec *iov, return 0; size -= pos; - if (size < iocb->ki_left) + if (size < iocb->ki_nbytes) nr_segs = iov_shorten((struct iovec *)iov, nr_segs, size); return generic_file_aio_read(iocb, iov, nr_segs, pos); } diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 0bd7a55a5f07..91ff089d3412 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -130,7 +130,6 @@ ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_ return -EINVAL; #else - VM_BUG_ON(iocb->ki_left != PAGE_SIZE); VM_BUG_ON(iocb->ki_nbytes != PAGE_SIZE); if (rw == READ || rw == KERNEL_READ) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 41000f223ca4..dd1a4901a54b 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2245,7 +2245,7 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb, file->f_path.dentry->d_name.name, (unsigned int)nr_segs); - if (iocb->ki_left == 0) + if (iocb->ki_nbytes == 0) return 0; appending = file->f_flags & O_APPEND ? 1 : 0; @@ -2296,7 +2296,7 @@ relock: can_do_direct = direct_io; ret = ocfs2_prepare_inode_for_write(file, ppos, - iocb->ki_left, appending, + iocb->ki_nbytes, appending, &can_do_direct, &has_refcount); if (ret < 0) { mlog_errno(ret); @@ -2304,7 +2304,7 @@ relock: } if (direct_io && !is_sync_kiocb(iocb)) - unaligned_dio = ocfs2_is_io_unaligned(inode, iocb->ki_left, + unaligned_dio = ocfs2_is_io_unaligned(inode, iocb->ki_nbytes, *ppos); /* diff --git a/fs/read_write.c b/fs/read_write.c index 122a3846d9e1..e3cd280b158c 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -367,7 +367,6 @@ ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *pp init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_left = len; kiocb.ki_nbytes = len; ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos); @@ -417,7 +416,6 @@ ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, lof init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_left = len; kiocb.ki_nbytes = len; ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos); @@ -599,7 +597,6 @@ static ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov, init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; - kiocb.ki_left = len; kiocb.ki_nbytes = len; ret = fn(&kiocb, iov, nr_segs, kiocb.ki_pos); diff --git a/fs/udf/file.c b/fs/udf/file.c index 29569dd08168..c02a27a19c6d 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -141,7 +141,7 @@ static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov, struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); int err, pos; - size_t count = iocb->ki_left; + size_t count = iocb->ki_nbytes; struct udf_inode_info *iinfo = UDF_I(inode); down_write(&iinfo->i_data_sem); diff --git a/include/linux/aio.h b/include/linux/aio.h index 8c8dd1d84d2e..7bb766e73968 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -50,11 +50,9 @@ struct kiocb { unsigned short ki_opcode; size_t ki_nbytes; /* copy of iocb->aio_nbytes */ char __user *ki_buf; /* remaining iocb->aio_buf */ - size_t ki_left; /* remaining bytes */ struct iovec ki_inline_vec; /* inline vector */ struct iovec *ki_iovec; unsigned long ki_nr_segs; - unsigned long ki_cur_seg; struct list_head ki_list; /* the aio core uses this * for cancellation */ diff --git a/mm/page_io.c b/mm/page_io.c index ba05b64e5d8d..8c79a4764be0 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -266,7 +266,6 @@ int __swap_writepage(struct page *page, struct writeback_control *wbc, init_sync_kiocb(&kiocb, swap_file); kiocb.ki_pos = page_file_offset(page); - kiocb.ki_left = PAGE_SIZE; kiocb.ki_nbytes = PAGE_SIZE; set_page_writeback(page); diff --git a/net/socket.c b/net/socket.c index 829b460acb87..fea902f2ba58 100644 --- a/net/socket.c +++ b/net/socket.c @@ -931,7 +931,7 @@ static ssize_t sock_aio_read(struct kiocb *iocb, const struct iovec *iov, if (pos != 0) return -ESPIPE; - if (iocb->ki_left == 0) /* Match SYS5 behaviour */ + if (iocb->ki_nbytes == 0) /* Match SYS5 behaviour */ return 0; -- cgit v1.2.3-71-gd317 From 8bc92afcf7f5c598001dd04e62d88f57f6e89e51 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Mon, 25 Feb 2013 16:36:27 -0800 Subject: aio: Kill unneeded kiocb members The old aio retry infrastucture needed to save the various arguments to to aio operations. But with the retry infrastructure gone, we can trim struct kiocb quite a bit. Signed-off-by: Kent Overstreet Cc: Zach Brown Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: Mark Fasheh Cc: Joel Becker Cc: Rusty Russell Cc: Jens Axboe Cc: Asai Thambi S P Cc: Selvan Mani Cc: Sam Bradshaw Cc: Jeff Moyer Cc: Al Viro Cc: Benjamin LaHaise Cc: Theodore Ts'o Signed-off-by: Benjamin LaHaise --- fs/aio.c | 69 +++++++++++++++++++++++++++++++---------------------- include/linux/aio.h | 11 ++------- 2 files changed, 42 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/fs/aio.c b/fs/aio.c index d00904db69b7..09fe1f334631 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -723,8 +723,6 @@ static void kiocb_free(struct kiocb *req) eventfd_ctx_put(req->ki_eventfd); if (req->ki_dtor) req->ki_dtor(req); - if (req->ki_iovec != &req->ki_inline_vec) - kfree(req->ki_iovec); kmem_cache_free(kiocb_cachep, req); } @@ -1054,24 +1052,26 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx) typedef ssize_t (aio_rw_op)(struct kiocb *, const struct iovec *, unsigned long, loff_t); -static ssize_t aio_setup_vectored_rw(int rw, struct kiocb *kiocb, bool compat) +static ssize_t aio_setup_vectored_rw(struct kiocb *kiocb, + int rw, char __user *buf, + unsigned long *nr_segs, + struct iovec **iovec, + bool compat) { ssize_t ret; - kiocb->ki_nr_segs = kiocb->ki_nbytes; + *nr_segs = kiocb->ki_nbytes; #ifdef CONFIG_COMPAT if (compat) ret = compat_rw_copy_check_uvector(rw, - (struct compat_iovec __user *)kiocb->ki_buf, - kiocb->ki_nr_segs, 1, &kiocb->ki_inline_vec, - &kiocb->ki_iovec); + (struct compat_iovec __user *)buf, + *nr_segs, 1, *iovec, iovec); else #endif ret = rw_copy_check_uvector(rw, - (struct iovec __user *)kiocb->ki_buf, - kiocb->ki_nr_segs, 1, &kiocb->ki_inline_vec, - &kiocb->ki_iovec); + (struct iovec __user *)buf, + *nr_segs, 1, *iovec, iovec); if (ret < 0) return ret; @@ -1080,15 +1080,17 @@ static ssize_t aio_setup_vectored_rw(int rw, struct kiocb *kiocb, bool compat) return 0; } -static ssize_t aio_setup_single_vector(int rw, struct kiocb *kiocb) +static ssize_t aio_setup_single_vector(struct kiocb *kiocb, + int rw, char __user *buf, + unsigned long *nr_segs, + struct iovec *iovec) { - if (unlikely(!access_ok(!rw, kiocb->ki_buf, kiocb->ki_nbytes))) + if (unlikely(!access_ok(!rw, buf, kiocb->ki_nbytes))) return -EFAULT; - kiocb->ki_iovec = &kiocb->ki_inline_vec; - kiocb->ki_iovec->iov_base = kiocb->ki_buf; - kiocb->ki_iovec->iov_len = kiocb->ki_nbytes; - kiocb->ki_nr_segs = 1; + iovec->iov_base = buf; + iovec->iov_len = kiocb->ki_nbytes; + *nr_segs = 1; return 0; } @@ -1097,15 +1099,18 @@ static ssize_t aio_setup_single_vector(int rw, struct kiocb *kiocb) * Performs the initial checks and aio retry method * setup for the kiocb at the time of io submission. */ -static ssize_t aio_run_iocb(struct kiocb *req, bool compat) +static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, + char __user *buf, bool compat) { struct file *file = req->ki_filp; ssize_t ret; + unsigned long nr_segs; int rw; fmode_t mode; aio_rw_op *rw_op; + struct iovec inline_vec, *iovec = &inline_vec; - switch (req->ki_opcode) { + switch (opcode) { case IOCB_CMD_PREAD: case IOCB_CMD_PREADV: mode = FMODE_READ; @@ -1126,16 +1131,21 @@ rw_common: if (!rw_op) return -EINVAL; - ret = (req->ki_opcode == IOCB_CMD_PREADV || - req->ki_opcode == IOCB_CMD_PWRITEV) - ? aio_setup_vectored_rw(rw, req, compat) - : aio_setup_single_vector(rw, req); + ret = (opcode == IOCB_CMD_PREADV || + opcode == IOCB_CMD_PWRITEV) + ? aio_setup_vectored_rw(req, rw, buf, &nr_segs, + &iovec, compat) + : aio_setup_single_vector(req, rw, buf, &nr_segs, + iovec); if (ret) return ret; ret = rw_verify_area(rw, file, &req->ki_pos, req->ki_nbytes); - if (ret < 0) + if (ret < 0) { + if (iovec != &inline_vec) + kfree(iovec); return ret; + } req->ki_nbytes = ret; @@ -1149,8 +1159,7 @@ rw_common: if (rw == WRITE) file_start_write(file); - ret = rw_op(req, req->ki_iovec, - req->ki_nr_segs, req->ki_pos); + ret = rw_op(req, iovec, nr_segs, req->ki_pos); if (rw == WRITE) file_end_write(file); @@ -1175,6 +1184,9 @@ rw_common: return -EINVAL; } + if (iovec != &inline_vec) + kfree(iovec); + if (ret != -EIOCBQUEUED) { /* * There's no easy way to restart the syscall since other AIO's @@ -1246,12 +1258,11 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, req->ki_obj.user = user_iocb; req->ki_user_data = iocb->aio_data; req->ki_pos = iocb->aio_offset; - - req->ki_buf = (char __user *)(unsigned long)iocb->aio_buf; req->ki_nbytes = iocb->aio_nbytes; - req->ki_opcode = iocb->aio_lio_opcode; - ret = aio_run_iocb(req, compat); + ret = aio_run_iocb(req, iocb->aio_lio_opcode, + (char __user *)(unsigned long)iocb->aio_buf, + compat); if (ret) goto out_put_req; diff --git a/include/linux/aio.h b/include/linux/aio.h index 7bb766e73968..b570472355d1 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -36,6 +36,7 @@ struct kiocb { struct kioctx *ki_ctx; /* NULL for sync ops */ kiocb_cancel_fn *ki_cancel; void (*ki_dtor)(struct kiocb *); + void *private; union { void __user *user; @@ -44,15 +45,7 @@ struct kiocb { __u64 ki_user_data; /* user's data for completion */ loff_t ki_pos; - - void *private; - /* State that we remember to be able to restart/retry */ - unsigned short ki_opcode; - size_t ki_nbytes; /* copy of iocb->aio_nbytes */ - char __user *ki_buf; /* remaining iocb->aio_buf */ - struct iovec ki_inline_vec; /* inline vector */ - struct iovec *ki_iovec; - unsigned long ki_nr_segs; + size_t ki_nbytes; /* copy of iocb->aio_nbytes */ struct list_head ki_list; /* the aio core uses this * for cancellation */ -- cgit v1.2.3-71-gd317 From 57282d8fd744072d6d6f18fa6ebe3cc1149015bf Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Mon, 13 May 2013 13:42:52 -0700 Subject: aio: Kill ki_users The kiocb refcount is only needed for cancellation - to ensure a kiocb isn't freed while a ki_cancel callback is running. But if we restrict ki_cancel callbacks to not block (which they currently don't), we can simply drop the refcount. Signed-off-by: Kent Overstreet Cc: Zach Brown Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: Mark Fasheh Cc: Joel Becker Cc: Rusty Russell Cc: Jens Axboe Cc: Asai Thambi S P Cc: Selvan Mani Cc: Sam Bradshaw Cc: Jeff Moyer Cc: Al Viro Cc: Benjamin LaHaise Cc: Theodore Ts'o Signed-off-by: Benjamin LaHaise --- fs/aio.c | 47 ++++++++++++----------------------------------- include/linux/aio.h | 5 ----- 2 files changed, 12 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/fs/aio.c b/fs/aio.c index 09fe1f334631..69a608a43760 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -361,7 +361,6 @@ EXPORT_SYMBOL(kiocb_set_cancel_fn); static int kiocb_cancel(struct kioctx *ctx, struct kiocb *kiocb) { kiocb_cancel_fn *old, *cancel; - int ret = -EINVAL; /* * Don't want to set kiocb->ki_cancel = KIOCB_CANCELLED unless it @@ -371,21 +370,13 @@ static int kiocb_cancel(struct kioctx *ctx, struct kiocb *kiocb) cancel = ACCESS_ONCE(kiocb->ki_cancel); do { if (!cancel || cancel == KIOCB_CANCELLED) - return ret; + return -EINVAL; old = cancel; cancel = cmpxchg(&kiocb->ki_cancel, old, KIOCB_CANCELLED); } while (cancel != old); - atomic_inc(&kiocb->ki_users); - spin_unlock_irq(&ctx->ctx_lock); - - ret = cancel(kiocb); - - spin_lock_irq(&ctx->ctx_lock); - aio_put_req(kiocb); - - return ret; + return cancel(kiocb); } static void free_ioctx_rcu(struct rcu_head *head) @@ -599,16 +590,16 @@ static void kill_ioctx(struct kioctx *ctx) /* wait_on_sync_kiocb: * Waits on the given sync kiocb to complete. */ -ssize_t wait_on_sync_kiocb(struct kiocb *iocb) +ssize_t wait_on_sync_kiocb(struct kiocb *req) { - while (atomic_read(&iocb->ki_users)) { + while (!req->ki_ctx) { set_current_state(TASK_UNINTERRUPTIBLE); - if (!atomic_read(&iocb->ki_users)) + if (req->ki_ctx) break; io_schedule(); } __set_current_state(TASK_RUNNING); - return iocb->ki_user_data; + return req->ki_user_data; } EXPORT_SYMBOL(wait_on_sync_kiocb); @@ -687,14 +678,8 @@ out: } /* aio_get_req - * Allocate a slot for an aio request. Increments the ki_users count - * of the kioctx so that the kioctx stays around until all requests are - * complete. Returns NULL if no requests are free. - * - * Returns with kiocb->ki_users set to 2. The io submit code path holds - * an extra reference while submitting the i/o. - * This prevents races between the aio code path referencing the - * req (after submitting it) and aio_complete() freeing the req. + * Allocate a slot for an aio request. + * Returns NULL if no requests are free. */ static inline struct kiocb *aio_get_req(struct kioctx *ctx) { @@ -707,7 +692,6 @@ static inline struct kiocb *aio_get_req(struct kioctx *ctx) if (unlikely(!req)) goto out_put; - atomic_set(&req->ki_users, 1); req->ki_ctx = ctx; return req; out_put: @@ -726,13 +710,6 @@ static void kiocb_free(struct kiocb *req) kmem_cache_free(kiocb_cachep, req); } -void aio_put_req(struct kiocb *req) -{ - if (atomic_dec_and_test(&req->ki_users)) - kiocb_free(req); -} -EXPORT_SYMBOL(aio_put_req); - static struct kioctx *lookup_ioctx(unsigned long ctx_id) { struct mm_struct *mm = current->mm; @@ -771,9 +748,9 @@ void aio_complete(struct kiocb *iocb, long res, long res2) * - the sync task helpfully left a reference to itself in the iocb */ if (is_sync_kiocb(iocb)) { - BUG_ON(atomic_read(&iocb->ki_users) != 1); iocb->ki_user_data = res; - atomic_set(&iocb->ki_users, 0); + smp_wmb(); + iocb->ki_ctx = ERR_PTR(-EXDEV); wake_up_process(iocb->ki_obj.tsk); return; } @@ -845,7 +822,7 @@ void aio_complete(struct kiocb *iocb, long res, long res2) eventfd_signal(iocb->ki_eventfd, 1); /* everything turned out well, dispose of the aiocb. */ - aio_put_req(iocb); + kiocb_free(iocb); /* * We have to order our ring_info tail store above and test @@ -1269,7 +1246,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, return 0; out_put_req: put_reqs_available(ctx, 1); - aio_put_req(req); + kiocb_free(req); return ret; } diff --git a/include/linux/aio.h b/include/linux/aio.h index b570472355d1..c4f07ffa1cbb 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -30,8 +30,6 @@ struct kiocb; typedef int (kiocb_cancel_fn)(struct kiocb *); struct kiocb { - atomic_t ki_users; - struct file *ki_filp; struct kioctx *ki_ctx; /* NULL for sync ops */ kiocb_cancel_fn *ki_cancel; @@ -65,7 +63,6 @@ static inline bool is_sync_kiocb(struct kiocb *kiocb) static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) { *kiocb = (struct kiocb) { - .ki_users = ATOMIC_INIT(1), .ki_ctx = NULL, .ki_filp = filp, .ki_obj.tsk = current, @@ -75,7 +72,6 @@ static inline void init_sync_kiocb(struct kiocb *kiocb, struct file *filp) /* prototypes */ #ifdef CONFIG_AIO extern ssize_t wait_on_sync_kiocb(struct kiocb *iocb); -extern void aio_put_req(struct kiocb *iocb); extern void aio_complete(struct kiocb *iocb, long res, long res2); struct mm_struct; extern void exit_aio(struct mm_struct *mm); @@ -84,7 +80,6 @@ extern long do_io_submit(aio_context_t ctx_id, long nr, void kiocb_set_cancel_fn(struct kiocb *req, kiocb_cancel_fn *cancel); #else static inline ssize_t wait_on_sync_kiocb(struct kiocb *iocb) { return 0; } -static inline void aio_put_req(struct kiocb *iocb) { } static inline void aio_complete(struct kiocb *iocb, long res, long res2) { } struct mm_struct; static inline void exit_aio(struct mm_struct *mm) { } -- cgit v1.2.3-71-gd317 From d29c445b635b3a03cf683cafcbae58a4ec1e1125 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Mon, 18 Mar 2013 11:09:26 -0700 Subject: aio: Kill ki_dtor sock_aio_dtor() is dead code - and stuff that does need to do cleanup can simply do it before calling aio_complete(). Signed-off-by: Kent Overstreet Cc: Zach Brown Cc: Felipe Balbi Cc: Greg Kroah-Hartman Cc: Mark Fasheh Cc: Joel Becker Cc: Rusty Russell Cc: Jens Axboe Cc: Asai Thambi S P Cc: Selvan Mani Cc: Sam Bradshaw Cc: Jeff Moyer Cc: Al Viro Cc: Benjamin LaHaise Cc: Theodore Ts'o Signed-off-by: Benjamin LaHaise --- fs/aio.c | 2 -- include/linux/aio.h | 1 - net/socket.c | 13 ++----------- 3 files changed, 2 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/fs/aio.c b/fs/aio.c index 69a608a43760..e46b1195191b 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -705,8 +705,6 @@ static void kiocb_free(struct kiocb *req) fput(req->ki_filp); if (req->ki_eventfd != NULL) eventfd_ctx_put(req->ki_eventfd); - if (req->ki_dtor) - req->ki_dtor(req); kmem_cache_free(kiocb_cachep, req); } diff --git a/include/linux/aio.h b/include/linux/aio.h index c4f07ffa1cbb..d9c92daa3944 100644 --- a/include/linux/aio.h +++ b/include/linux/aio.h @@ -33,7 +33,6 @@ struct kiocb { struct file *ki_filp; struct kioctx *ki_ctx; /* NULL for sync ops */ kiocb_cancel_fn *ki_cancel; - void (*ki_dtor)(struct kiocb *); void *private; union { diff --git a/net/socket.c b/net/socket.c index fea902f2ba58..06a082e0e49c 100644 --- a/net/socket.c +++ b/net/socket.c @@ -854,11 +854,6 @@ int kernel_recvmsg(struct socket *sock, struct msghdr *msg, } EXPORT_SYMBOL(kernel_recvmsg); -static void sock_aio_dtor(struct kiocb *iocb) -{ - kfree(iocb->private); -} - static ssize_t sock_sendpage(struct file *file, struct page *page, int offset, size_t size, loff_t *ppos, int more) { @@ -889,12 +884,8 @@ static ssize_t sock_splice_read(struct file *file, loff_t *ppos, static struct sock_iocb *alloc_sock_iocb(struct kiocb *iocb, struct sock_iocb *siocb) { - if (!is_sync_kiocb(iocb)) { - siocb = kmalloc(sizeof(*siocb), GFP_KERNEL); - if (!siocb) - return NULL; - iocb->ki_dtor = sock_aio_dtor; - } + if (!is_sync_kiocb(iocb)) + BUG(); siocb->kiocb = iocb; iocb->private = siocb; -- cgit v1.2.3-71-gd317 From db446a08c23d5475e6b08c87acca79ebb20f283c Mon Sep 17 00:00:00 2001 From: Benjamin LaHaise Date: Tue, 30 Jul 2013 12:54:40 -0400 Subject: aio: convert the ioctx list to table lookup v3 On Wed, Jun 12, 2013 at 11:14:40AM -0700, Kent Overstreet wrote: > On Mon, Apr 15, 2013 at 02:40:55PM +0300, Octavian Purdila wrote: > > When using a large number of threads performing AIO operations the > > IOCTX list may get a significant number of entries which will cause > > significant overhead. For example, when running this fio script: > > > > rw=randrw; size=256k ;directory=/mnt/fio; ioengine=libaio; iodepth=1 > > blocksize=1024; numjobs=512; thread; loops=100 > > > > on an EXT2 filesystem mounted on top of a ramdisk we can observe up to > > 30% CPU time spent by lookup_ioctx: > > > > 32.51% [guest.kernel] [g] lookup_ioctx > > 9.19% [guest.kernel] [g] __lock_acquire.isra.28 > > 4.40% [guest.kernel] [g] lock_release > > 4.19% [guest.kernel] [g] sched_clock_local > > 3.86% [guest.kernel] [g] local_clock > > 3.68% [guest.kernel] [g] native_sched_clock > > 3.08% [guest.kernel] [g] sched_clock_cpu > > 2.64% [guest.kernel] [g] lock_release_holdtime.part.11 > > 2.60% [guest.kernel] [g] memcpy > > 2.33% [guest.kernel] [g] lock_acquired > > 2.25% [guest.kernel] [g] lock_acquire > > 1.84% [guest.kernel] [g] do_io_submit > > > > This patchs converts the ioctx list to a radix tree. For a performance > > comparison the above FIO script was run on a 2 sockets 8 core > > machine. This are the results (average and %rsd of 10 runs) for the > > original list based implementation and for the radix tree based > > implementation: > > > > cores 1 2 4 8 16 32 > > list 109376 ms 69119 ms 35682 ms 22671 ms 19724 ms 16408 ms > > %rsd 0.69% 1.15% 1.17% 1.21% 1.71% 1.43% > > radix 73651 ms 41748 ms 23028 ms 16766 ms 15232 ms 13787 ms > > %rsd 1.19% 0.98% 0.69% 1.13% 0.72% 0.75% > > % of radix > > relative 66.12% 65.59% 66.63% 72.31% 77.26% 83.66% > > to list > > > > To consider the impact of the patch on the typical case of having > > only one ctx per process the following FIO script was run: > > > > rw=randrw; size=100m ;directory=/mnt/fio; ioengine=libaio; iodepth=1 > > blocksize=1024; numjobs=1; thread; loops=100 > > > > on the same system and the results are the following: > > > > list 58892 ms > > %rsd 0.91% > > radix 59404 ms > > %rsd 0.81% > > % of radix > > relative 100.87% > > to list > > So, I was just doing some benchmarking/profiling to get ready to send > out the aio patches I've got for 3.11 - and it looks like your patch is > causing a ~1.5% throughput regression in my testing :/ ... I've got an alternate approach for fixing this wart in lookup_ioctx()... Instead of using an rbtree, just use the reserved id in the ring buffer header to index an array pointing the ioctx. It's not finished yet, and it needs to be tidied up, but is most of the way there. -ben -- "Thought is the essence of where you are now." -- kmo> And, a rework of Ben's code, but this was entirely his idea kmo> -Kent bcrl> And fix the code to use the right mm_struct in kill_ioctx(), actually free memory. Signed-off-by: Benjamin LaHaise --- fs/aio.c | 136 +++++++++++++++++++++++++++++++++++++++-------- include/linux/mm_types.h | 5 +- kernel/fork.c | 2 +- 3 files changed, 118 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/fs/aio.c b/fs/aio.c index 945dd0d072f3..52f200ebef07 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -66,6 +66,12 @@ struct aio_ring { #define AIO_RING_PAGES 8 +struct kioctx_table { + struct rcu_head rcu; + unsigned nr; + struct kioctx *table[]; +}; + struct kioctx_cpu { unsigned reqs_available; }; @@ -74,9 +80,7 @@ struct kioctx { struct percpu_ref users; atomic_t dead; - /* This needs improving */ unsigned long user_id; - struct hlist_node list; struct __percpu kioctx_cpu *cpu; @@ -135,6 +139,8 @@ struct kioctx { struct page *internal_pages[AIO_RING_PAGES]; struct file *aio_ring_file; + + unsigned id; }; /*------ sysctl variables----*/ @@ -326,7 +332,7 @@ static int aio_setup_ring(struct kioctx *ctx) ring = kmap_atomic(ctx->ring_pages[0]); ring->nr = nr_events; /* user copy */ - ring->id = ctx->user_id; + ring->id = ~0U; ring->head = ring->tail = 0; ring->magic = AIO_RING_MAGIC; ring->compat_features = AIO_RING_COMPAT_FEATURES; @@ -462,6 +468,58 @@ static void free_ioctx_ref(struct percpu_ref *ref) schedule_work(&ctx->free_work); } +static int ioctx_add_table(struct kioctx *ctx, struct mm_struct *mm) +{ + unsigned i, new_nr; + struct kioctx_table *table, *old; + struct aio_ring *ring; + + spin_lock(&mm->ioctx_lock); + table = rcu_dereference(mm->ioctx_table); + + while (1) { + if (table) + for (i = 0; i < table->nr; i++) + if (!table->table[i]) { + ctx->id = i; + table->table[i] = ctx; + spin_unlock(&mm->ioctx_lock); + + ring = kmap_atomic(ctx->ring_pages[0]); + ring->id = ctx->id; + kunmap_atomic(ring); + return 0; + } + + new_nr = (table ? table->nr : 1) * 4; + + spin_unlock(&mm->ioctx_lock); + + table = kzalloc(sizeof(*table) + sizeof(struct kioctx *) * + new_nr, GFP_KERNEL); + if (!table) + return -ENOMEM; + + table->nr = new_nr; + + spin_lock(&mm->ioctx_lock); + old = rcu_dereference(mm->ioctx_table); + + if (!old) { + rcu_assign_pointer(mm->ioctx_table, table); + } else if (table->nr > old->nr) { + memcpy(table->table, old->table, + old->nr * sizeof(struct kioctx *)); + + rcu_assign_pointer(mm->ioctx_table, table); + kfree_rcu(old, rcu); + } else { + kfree(table); + table = old; + } + } +} + /* ioctx_alloc * Allocates and initializes an ioctx. Returns an ERR_PTR if it failed. */ @@ -520,6 +578,10 @@ static struct kioctx *ioctx_alloc(unsigned nr_events) ctx->req_batch = (ctx->nr_events - 1) / (num_possible_cpus() * 4); BUG_ON(!ctx->req_batch); + err = ioctx_add_table(ctx, mm); + if (err) + goto out_cleanup_noerr; + /* limit the number of system wide aios */ spin_lock(&aio_nr_lock); if (aio_nr + nr_events > (aio_max_nr * 2UL) || @@ -532,17 +594,13 @@ static struct kioctx *ioctx_alloc(unsigned nr_events) percpu_ref_get(&ctx->users); /* io_setup() will drop this ref */ - /* now link into global list. */ - spin_lock(&mm->ioctx_lock); - hlist_add_head_rcu(&ctx->list, &mm->ioctx_list); - spin_unlock(&mm->ioctx_lock); - pr_debug("allocated ioctx %p[%ld]: mm=%p mask=0x%x\n", ctx, ctx->user_id, mm, ctx->nr_events); return ctx; out_cleanup: err = -EAGAIN; +out_cleanup_noerr: aio_free_ring(ctx); out_freepcpu: free_percpu(ctx->cpu); @@ -561,10 +619,18 @@ out_freectx: * when the processes owning a context have all exited to encourage * the rapid destruction of the kioctx. */ -static void kill_ioctx(struct kioctx *ctx) +static void kill_ioctx(struct mm_struct *mm, struct kioctx *ctx) { if (!atomic_xchg(&ctx->dead, 1)) { - hlist_del_rcu(&ctx->list); + struct kioctx_table *table; + + spin_lock(&mm->ioctx_lock); + table = rcu_dereference(mm->ioctx_table); + + WARN_ON(ctx != table->table[ctx->id]); + table->table[ctx->id] = NULL; + spin_unlock(&mm->ioctx_lock); + /* percpu_ref_kill() will do the necessary call_rcu() */ wake_up_all(&ctx->wait); @@ -613,10 +679,28 @@ EXPORT_SYMBOL(wait_on_sync_kiocb); */ void exit_aio(struct mm_struct *mm) { + struct kioctx_table *table; struct kioctx *ctx; - struct hlist_node *n; + unsigned i = 0; + + while (1) { + rcu_read_lock(); + table = rcu_dereference(mm->ioctx_table); + + do { + if (!table || i >= table->nr) { + rcu_read_unlock(); + rcu_assign_pointer(mm->ioctx_table, NULL); + if (table) + kfree(table); + return; + } + + ctx = table->table[i++]; + } while (!ctx); + + rcu_read_unlock(); - hlist_for_each_entry_safe(ctx, n, &mm->ioctx_list, list) { /* * We don't need to bother with munmap() here - * exit_mmap(mm) is coming and it'll unmap everything. @@ -627,7 +711,7 @@ void exit_aio(struct mm_struct *mm) */ ctx->mmap_size = 0; - kill_ioctx(ctx); + kill_ioctx(mm, ctx); } } @@ -710,19 +794,27 @@ static void kiocb_free(struct kiocb *req) static struct kioctx *lookup_ioctx(unsigned long ctx_id) { + struct aio_ring __user *ring = (void __user *)ctx_id; struct mm_struct *mm = current->mm; struct kioctx *ctx, *ret = NULL; + struct kioctx_table *table; + unsigned id; + + if (get_user(id, &ring->id)) + return NULL; rcu_read_lock(); + table = rcu_dereference(mm->ioctx_table); - hlist_for_each_entry_rcu(ctx, &mm->ioctx_list, list) { - if (ctx->user_id == ctx_id) { - percpu_ref_get(&ctx->users); - ret = ctx; - break; - } - } + if (!table || id >= table->nr) + goto out; + ctx = table->table[id]; + if (ctx->user_id == ctx_id) { + percpu_ref_get(&ctx->users); + ret = ctx; + } +out: rcu_read_unlock(); return ret; } @@ -998,7 +1090,7 @@ SYSCALL_DEFINE2(io_setup, unsigned, nr_events, aio_context_t __user *, ctxp) if (!IS_ERR(ioctx)) { ret = put_user(ioctx->user_id, ctxp); if (ret) - kill_ioctx(ioctx); + kill_ioctx(current->mm, ioctx); percpu_ref_put(&ioctx->users); } @@ -1016,7 +1108,7 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx) { struct kioctx *ioctx = lookup_ioctx(ctx); if (likely(NULL != ioctx)) { - kill_ioctx(ioctx); + kill_ioctx(current->mm, ioctx); percpu_ref_put(&ioctx->users); return 0; } diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index fb425aa16c01..da8cf5cc1aa6 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -322,6 +322,7 @@ struct mm_rss_stat { atomic_long_t count[NR_MM_COUNTERS]; }; +struct kioctx_table; struct mm_struct { struct vm_area_struct * mmap; /* list of VMAs */ struct rb_root mm_rb; @@ -382,8 +383,8 @@ struct mm_struct { struct core_state *core_state; /* coredumping support */ #ifdef CONFIG_AIO - spinlock_t ioctx_lock; - struct hlist_head ioctx_list; + spinlock_t ioctx_lock; + struct kioctx_table __rcu *ioctx_table; #endif #ifdef CONFIG_MM_OWNER /* diff --git a/kernel/fork.c b/kernel/fork.c index 66635c80a813..db5f541c5488 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -522,7 +522,7 @@ static void mm_init_aio(struct mm_struct *mm) { #ifdef CONFIG_AIO spin_lock_init(&mm->ioctx_lock); - INIT_HLIST_HEAD(&mm->ioctx_list); + mm->ioctx_table = NULL; #endif } -- cgit v1.2.3-71-gd317 From cfca7644d7959e1f50cb132306d1557bf6c2da57 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Jul 2013 12:14:46 -0300 Subject: [media] V4L: Rename v4l2_async_bus_* to v4l2_async_match_* enum v4l2_async_bus_type also selects a method subdevs are matched in the notification handlers, rename it to v4l2_async_match_type so V4L2_ASYNC_MATCH_OF entry can be further added for matching by device tree node pointer. Acked-and-tested-by: Lad, Prabhakar Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- .../platform/soc_camera/sh_mobile_ceu_camera.c | 6 ++--- drivers/media/platform/soc_camera/soc_camera.c | 2 +- drivers/media/v4l2-core/v4l2-async.c | 26 +++++++++++----------- include/media/v4l2-async.h | 12 +++++----- 4 files changed, 23 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index f2de0066089a..dae9716e34b1 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -1837,9 +1837,9 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) for (j = 0; pcdev->pdata->asd_sizes[j]; j++) { for (i = 0; i < pcdev->pdata->asd_sizes[j]; i++, asd++) { dev_dbg(&pdev->dev, "%s(): subdev #%d, type %u\n", - __func__, i, (*asd)->bus_type); - if ((*asd)->bus_type == V4L2_ASYNC_BUS_PLATFORM && - !strncmp(name, (*asd)->match.platform.name, + __func__, i, (*asd)->match_type); + if ((*asd)->match_type == V4L2_ASYNC_MATCH_DEVNAME && + !strncmp(name, (*asd)->match.device_name.name, sizeof(name) - 1)) { pcdev->csi2_asd = *asd; break; diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 2dd0e5272941..8af572bcf03a 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1475,7 +1475,7 @@ static int scan_async_group(struct soc_camera_host *ici, break; } - if (i == size || asd[i]->bus_type != V4L2_ASYNC_BUS_I2C) { + if (i == size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) { /* All useless */ dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n"); return -ENODEV; diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index ff87c299f640..86934ca73357 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -34,9 +34,9 @@ static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd) #endif } -static bool match_platform(struct device *dev, struct v4l2_async_subdev *asd) +static bool match_devname(struct device *dev, struct v4l2_async_subdev *asd) { - return !strcmp(asd->match.platform.name, dev_name(dev)); + return !strcmp(asd->match.device_name.name, dev_name(dev)); } static LIST_HEAD(subdev_list); @@ -53,17 +53,17 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier * list_for_each_entry(asd, ¬ifier->waiting, list) { /* bus_type has been verified valid before */ - switch (asd->bus_type) { - case V4L2_ASYNC_BUS_CUSTOM: + switch (asd->match_type) { + case V4L2_ASYNC_MATCH_CUSTOM: match = asd->match.custom.match; if (!match) /* Match always */ return asd; break; - case V4L2_ASYNC_BUS_PLATFORM: - match = match_platform; + case V4L2_ASYNC_MATCH_DEVNAME: + match = match_devname; break; - case V4L2_ASYNC_BUS_I2C: + case V4L2_ASYNC_MATCH_I2C: match = match_i2c; break; default: @@ -141,15 +141,15 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, for (i = 0; i < notifier->num_subdevs; i++) { asd = notifier->subdev[i]; - switch (asd->bus_type) { - case V4L2_ASYNC_BUS_CUSTOM: - case V4L2_ASYNC_BUS_PLATFORM: - case V4L2_ASYNC_BUS_I2C: + switch (asd->match_type) { + case V4L2_ASYNC_MATCH_CUSTOM: + case V4L2_ASYNC_MATCH_DEVNAME: + case V4L2_ASYNC_MATCH_I2C: break; default: dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL, - "Invalid bus-type %u on %p\n", - asd->bus_type, asd); + "Invalid match type %u on %p\n", + asd->match_type, asd); return -EINVAL; } list_add_tail(&asd->list, ¬ifier->waiting); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index c3ec6ac75f7e..33e3b2a30ecb 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -22,10 +22,10 @@ struct v4l2_async_notifier; /* A random max subdevice number, used to allocate an array on stack */ #define V4L2_MAX_SUBDEVS 128U -enum v4l2_async_bus_type { - V4L2_ASYNC_BUS_CUSTOM, - V4L2_ASYNC_BUS_PLATFORM, - V4L2_ASYNC_BUS_I2C, +enum v4l2_async_match_type { + V4L2_ASYNC_MATCH_CUSTOM, + V4L2_ASYNC_MATCH_DEVNAME, + V4L2_ASYNC_MATCH_I2C, }; /** @@ -36,11 +36,11 @@ enum v4l2_async_bus_type { * probed, to a notifier->waiting list */ struct v4l2_async_subdev { - enum v4l2_async_bus_type bus_type; + enum v4l2_async_match_type match_type; union { struct { const char *name; - } platform; + } device_name; struct { int adapter_id; unsigned short address; -- cgit v1.2.3-71-gd317 From e7359f8e660882fb2168609638163561eb2f50f0 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Jul 2013 12:21:29 -0300 Subject: [media] V4L: Add V4L2_ASYNC_MATCH_OF subdev matching type Add support for matching by device_node pointer. This allows the notifier user to simply pass a list of device_node pointers corresponding to sub-devices. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Lad, Prabhakar Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 9 +++++++++ include/media/v4l2-async.h | 5 +++++ 2 files changed, 14 insertions(+) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 86934ca73357..9f91013a600d 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -39,6 +39,11 @@ static bool match_devname(struct device *dev, struct v4l2_async_subdev *asd) return !strcmp(asd->match.device_name.name, dev_name(dev)); } +static bool match_of(struct device *dev, struct v4l2_async_subdev *asd) +{ + return dev->of_node == asd->match.of.node; +} + static LIST_HEAD(subdev_list); static LIST_HEAD(notifier_list); static DEFINE_MUTEX(list_lock); @@ -66,6 +71,9 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier * case V4L2_ASYNC_MATCH_I2C: match = match_i2c; break; + case V4L2_ASYNC_MATCH_OF: + match = match_of; + break; default: /* Cannot happen, unless someone breaks us */ WARN_ON(true); @@ -145,6 +153,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, case V4L2_ASYNC_MATCH_CUSTOM: case V4L2_ASYNC_MATCH_DEVNAME: case V4L2_ASYNC_MATCH_I2C: + case V4L2_ASYNC_MATCH_OF: break; default: dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL, diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 33e3b2a30ecb..82b29813f8be 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -15,6 +15,7 @@ #include struct device; +struct device_node; struct v4l2_device; struct v4l2_subdev; struct v4l2_async_notifier; @@ -26,6 +27,7 @@ enum v4l2_async_match_type { V4L2_ASYNC_MATCH_CUSTOM, V4L2_ASYNC_MATCH_DEVNAME, V4L2_ASYNC_MATCH_I2C, + V4L2_ASYNC_MATCH_OF, }; /** @@ -38,6 +40,9 @@ enum v4l2_async_match_type { struct v4l2_async_subdev { enum v4l2_async_match_type match_type; union { + struct { + const struct device_node *node; + } of; struct { const char *name; } device_name; -- cgit v1.2.3-71-gd317 From e8419d0890efaccb93f9f274d9ba0aae91210e8c Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 19 Jul 2013 12:31:10 -0300 Subject: [media] V4L: Rename subdev field of struct v4l2_async_notifier This is a purely cosmetic change. Since the 'subdev' member points to an array of subdevs make it more explicit by renaming to the plural form. Acked-and-tested-by: Lad, Prabhakar Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/davinci/vpif_capture.c | 2 +- drivers/media/platform/davinci/vpif_display.c | 2 +- drivers/media/platform/soc_camera/soc_camera.c | 2 +- drivers/media/v4l2-core/v4l2-async.c | 2 +- include/media/v4l2-async.h | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index b11d7a74497e..7fbde6d790b5 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -2168,7 +2168,7 @@ static __init int vpif_probe(struct platform_device *pdev) } vpif_probe_complete(); } else { - vpif_obj.notifier.subdev = vpif_obj.config->asd; + vpif_obj.notifier.subdevs = vpif_obj.config->asd; vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; vpif_obj.notifier.bound = vpif_async_bound; vpif_obj.notifier.complete = vpif_async_complete; diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index c2ff06745fdc..6336dfc86482 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1832,7 +1832,7 @@ static __init int vpif_probe(struct platform_device *pdev) } vpif_probe_complete(); } else { - vpif_obj.notifier.subdev = vpif_obj.config->asd; + vpif_obj.notifier.subdevs = vpif_obj.config->asd; vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; vpif_obj.notifier.bound = vpif_async_bound; vpif_obj.notifier.complete = vpif_async_complete; diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 8af572bcf03a..4b42572253e0 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1501,7 +1501,7 @@ static int scan_async_group(struct soc_camera_host *ici, return -ENOMEM; } - sasc->notifier.subdev = asd; + sasc->notifier.subdevs = asd; sasc->notifier.num_subdevs = size; sasc->notifier.bound = soc_camera_async_bound; sasc->notifier.unbind = soc_camera_async_unbind; diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 9f91013a600d..ed31a655b9b7 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -147,7 +147,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, INIT_LIST_HEAD(¬ifier->done); for (i = 0; i < notifier->num_subdevs; i++) { - asd = notifier->subdev[i]; + asd = notifier->subdevs[i]; switch (asd->match_type) { case V4L2_ASYNC_MATCH_CUSTOM: diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 82b29813f8be..8fac8eaca51a 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -77,7 +77,7 @@ struct v4l2_async_subdev_list { /** * v4l2_async_notifier - v4l2_device notifier data * @num_subdevs:number of subdevices - * @subdev: array of pointers to subdevice descriptors + * @subdevs: array of pointers to subdevice descriptors * @v4l2_dev: pointer to struct v4l2_device * @waiting: list of struct v4l2_async_subdev, waiting for their drivers * @done: list of struct v4l2_async_subdev_list, already probed @@ -88,7 +88,7 @@ struct v4l2_async_subdev_list { */ struct v4l2_async_notifier { unsigned int num_subdevs; - struct v4l2_async_subdev **subdev; + struct v4l2_async_subdev **subdevs; struct v4l2_device *v4l2_dev; struct list_head waiting; struct list_head done; -- cgit v1.2.3-71-gd317 From b426b3a660c85faf6e1ca1c92c6d43577022aa3b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 22 Jul 2013 08:01:33 -0300 Subject: [media] V4L: Merge struct v4l2_async_subdev_list with struct v4l2_subdev By integrating the v4l2-async API internals a bit more with the core overall the v4l2-async code becomes a bit simpler and easier to follow. Acked-and-tested-by: Lad, Prabhakar Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 67 ++++++++++++++++-------------------- include/media/v4l2-async.h | 15 +------- include/media/v4l2-subdev.h | 13 ++++--- 3 files changed, 36 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index ed31a655b9b7..b350ab99652c 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -49,12 +49,10 @@ static LIST_HEAD(notifier_list); static DEFINE_MUTEX(list_lock); static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier, - struct v4l2_async_subdev_list *asdl) + struct v4l2_subdev *sd) { - struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); struct v4l2_async_subdev *asd; - bool (*match)(struct device *, - struct v4l2_async_subdev *); + bool (*match)(struct device *, struct v4l2_async_subdev *); list_for_each_entry(asd, ¬ifier->waiting, list) { /* bus_type has been verified valid before */ @@ -89,16 +87,15 @@ static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier * } static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, - struct v4l2_async_subdev_list *asdl, + struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { - struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); int ret; /* Remove from the waiting list */ list_del(&asd->list); - asdl->asd = asd; - asdl->notifier = notifier; + sd->asd = asd; + sd->notifier = notifier; if (notifier->bound) { ret = notifier->bound(notifier, sd, asd); @@ -106,7 +103,7 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, return ret; } /* Move from the global subdevice list to notifier's done */ - list_move(&asdl->list, ¬ifier->done); + list_move(&sd->async_list, ¬ifier->done); ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd); if (ret < 0) { @@ -121,21 +118,19 @@ static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, return 0; } -static void v4l2_async_cleanup(struct v4l2_async_subdev_list *asdl) +static void v4l2_async_cleanup(struct v4l2_subdev *sd) { - struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); - v4l2_device_unregister_subdev(sd); - /* Subdevice driver will reprobe and put asdl back onto the list */ - list_del_init(&asdl->list); - asdl->asd = NULL; + /* Subdevice driver will reprobe and put the subdev back onto the list */ + list_del_init(&sd->async_list); + sd->asd = NULL; sd->dev = NULL; } int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, struct v4l2_async_notifier *notifier) { - struct v4l2_async_subdev_list *asdl, *tmp; + struct v4l2_subdev *sd, *tmp; struct v4l2_async_subdev *asd; int i; @@ -169,14 +164,14 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, /* Keep also completed notifiers on the list */ list_add(¬ifier->list, ¬ifier_list); - list_for_each_entry_safe(asdl, tmp, &subdev_list, list) { + list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) { int ret; - asd = v4l2_async_belongs(notifier, asdl); + asd = v4l2_async_belongs(notifier, sd); if (!asd) continue; - ret = v4l2_async_test_notify(notifier, asdl, asd); + ret = v4l2_async_test_notify(notifier, sd, asd); if (ret < 0) { mutex_unlock(&list_lock); return ret; @@ -191,7 +186,7 @@ EXPORT_SYMBOL(v4l2_async_notifier_register); void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) { - struct v4l2_async_subdev_list *asdl, *tmp; + struct v4l2_subdev *sd, *tmp; unsigned int notif_n_subdev = notifier->num_subdevs; unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS); struct device *dev[n_subdev]; @@ -201,18 +196,16 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) list_del(¬ifier->list); - list_for_each_entry_safe(asdl, tmp, ¬ifier->done, list) { - struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); - + list_for_each_entry_safe(sd, tmp, ¬ifier->done, list) { dev[i] = get_device(sd->dev); - v4l2_async_cleanup(asdl); + v4l2_async_cleanup(sd); /* If we handled USB devices, we'd have to lock the parent too */ device_release_driver(dev[i++]); if (notifier->unbind) - notifier->unbind(notifier, sd, sd->asdl.asd); + notifier->unbind(notifier, sd, sd->asd); } mutex_unlock(&list_lock); @@ -241,24 +234,23 @@ EXPORT_SYMBOL(v4l2_async_notifier_unregister); int v4l2_async_register_subdev(struct v4l2_subdev *sd) { - struct v4l2_async_subdev_list *asdl = &sd->asdl; struct v4l2_async_notifier *notifier; mutex_lock(&list_lock); - INIT_LIST_HEAD(&asdl->list); + INIT_LIST_HEAD(&sd->async_list); list_for_each_entry(notifier, ¬ifier_list, list) { - struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, asdl); + struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd); if (asd) { - int ret = v4l2_async_test_notify(notifier, asdl, asd); + int ret = v4l2_async_test_notify(notifier, sd, asd); mutex_unlock(&list_lock); return ret; } } /* None matched, wait for hot-plugging */ - list_add(&asdl->list, &subdev_list); + list_add(&sd->async_list, &subdev_list); mutex_unlock(&list_lock); @@ -268,23 +260,22 @@ EXPORT_SYMBOL(v4l2_async_register_subdev); void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) { - struct v4l2_async_subdev_list *asdl = &sd->asdl; - struct v4l2_async_notifier *notifier = asdl->notifier; + struct v4l2_async_notifier *notifier = sd->notifier; - if (!asdl->asd) { - if (!list_empty(&asdl->list)) - v4l2_async_cleanup(asdl); + if (!sd->asd) { + if (!list_empty(&sd->async_list)) + v4l2_async_cleanup(sd); return; } mutex_lock(&list_lock); - list_add(&asdl->asd->list, ¬ifier->waiting); + list_add(&sd->asd->list, ¬ifier->waiting); - v4l2_async_cleanup(asdl); + v4l2_async_cleanup(sd); if (notifier->unbind) - notifier->unbind(notifier, sd, sd->asdl.asd); + notifier->unbind(notifier, sd, sd->asd); mutex_unlock(&list_lock); } diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 8fac8eaca51a..768356917bea 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -61,26 +61,13 @@ struct v4l2_async_subdev { struct list_head list; }; -/** - * v4l2_async_subdev_list - provided by subdevices - * @list: links struct v4l2_async_subdev_list objects to a global list - * before probing, and onto notifier->done after probing - * @asd: pointer to respective struct v4l2_async_subdev - * @notifier: pointer to managing notifier - */ -struct v4l2_async_subdev_list { - struct list_head list; - struct v4l2_async_subdev *asd; - struct v4l2_async_notifier *notifier; -}; - /** * v4l2_async_notifier - v4l2_device notifier data * @num_subdevs:number of subdevices * @subdevs: array of pointers to subdevice descriptors * @v4l2_dev: pointer to struct v4l2_device * @waiting: list of struct v4l2_async_subdev, waiting for their drivers - * @done: list of struct v4l2_async_subdev_list, already probed + * @done: list of struct v4l2_subdev, already probed * @list: member in a global list of notifiers * @bound: a subdevice driver has successfully probed one of subdevices * @complete: all subdevices have been probed successfully diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 3250cc5e7925..bfda0fe9aeb0 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -586,15 +586,14 @@ struct v4l2_subdev { struct video_device *devnode; /* pointer to the physical device, if any */ struct device *dev; - struct v4l2_async_subdev_list asdl; + /* Links this subdev to a global subdev_list or @notifier->done list. */ + struct list_head async_list; + /* Pointer to respective struct v4l2_async_subdev. */ + struct v4l2_async_subdev *asd; + /* Pointer to the managing notifier. */ + struct v4l2_async_notifier *notifier; }; -static inline struct v4l2_subdev *v4l2_async_to_subdev( - struct v4l2_async_subdev_list *asdl) -{ - return container_of(asdl, struct v4l2_subdev, asdl); -} - #define media_entity_to_v4l2_subdev(ent) \ container_of(ent, struct v4l2_subdev, entity) #define vdev_to_v4l2_subdev(vdev) \ -- cgit v1.2.3-71-gd317 From 60ff779c4abba37a31bd8624ef45026f7fb1b70c Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Thu, 25 Jul 2013 10:54:24 +0200 Subject: 9p: client: remove unused code and any reference to "cancelled" function This patch reverts commit 80b45261a0b263536b043c5ccfc4ba4fc27c2acc which was implementing a 'cancelled' functionality to notify that a cancelled request will not be replied. This implementation was not used anywhere and therefore removed. Signed-off-by: Andi Shyti Signed-off-by: David S. Miller --- include/net/9p/transport.h | 3 --- net/9p/client.c | 9 ++------- 2 files changed, 2 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/9p/transport.h b/include/net/9p/transport.h index d9fa68f26c41..9a36d9297114 100644 --- a/include/net/9p/transport.h +++ b/include/net/9p/transport.h @@ -40,8 +40,6 @@ * @close: member function to discard a connection on this transport * @request: member function to issue a request to the transport * @cancel: member function to cancel a request (if it hasn't been sent) - * @cancelled: member function to notify that a cancelled request will not - * not receive a reply * * This is the basic API for a transport module which is registered by the * transport module with the 9P core network module and used by the client @@ -60,7 +58,6 @@ struct p9_trans_module { void (*close) (struct p9_client *); int (*request) (struct p9_client *, struct p9_req_t *req); int (*cancel) (struct p9_client *, struct p9_req_t *req); - int (*cancelled)(struct p9_client *, struct p9_req_t *req); int (*zc_request)(struct p9_client *, struct p9_req_t *, char *, char *, int , int, int, int); }; diff --git a/net/9p/client.c b/net/9p/client.c index 8b93cae2d11d..ba93bdab2701 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -658,17 +658,12 @@ static int p9_client_flush(struct p9_client *c, struct p9_req_t *oldreq) /* * if we haven't received a response for oldreq, - * remove it from the list, and notify the transport - * layer that the reply will never arrive. + * remove it from the list */ - spin_lock(&c->lock); if (oldreq->status == REQ_STATUS_FLSH) { + spin_lock(&c->lock); list_del(&oldreq->req_list); spin_unlock(&c->lock); - if (c->trans_mod->cancelled) - c->trans_mod->cancelled(c, req); - } else { - spin_unlock(&c->lock); } p9_free_req(c, req); -- cgit v1.2.3-71-gd317 From df04b6242a584ab3dcfe89221775085f60ba83dd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 12 Jul 2013 13:32:02 +0200 Subject: mfd: twl6040: Remove support for legacy (pdata) mode TWL6040 is used only with OMAP4/5 SoCs and they can only boot in in DT mode. The support for pdata/legacy boot can be removed. Add TODO comment to the header file that all pdata struct can be removed in the next merge window (after the sub driver updates are in). Signed-off-by: Peter Ujfalusi Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/twl6040.c | 55 +++++++++++---------------------------------- include/linux/mfd/twl6040.h | 1 + 2 files changed, 14 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index 492ee2cd3400..a4034ed66dc0 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -44,17 +44,12 @@ #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1) #define TWL6040_NUM_SUPPLIES (2) -static bool twl6040_has_vibra(struct twl6040_platform_data *pdata, - struct device_node *node) +static bool twl6040_has_vibra(struct device_node *node) { - if (pdata && pdata->vibra) - return true; - #ifdef CONFIG_OF if (of_find_node_by_name(node, "vibra")) return true; #endif - return false; } @@ -520,14 +515,13 @@ static struct regmap_irq_chip twl6040_irq_chip = { static int twl6040_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct twl6040_platform_data *pdata = client->dev.platform_data; struct device_node *node = client->dev.of_node; struct twl6040 *twl6040; struct mfd_cell *cell = NULL; int irq, ret, children = 0; - if (!pdata && !node) { - dev_err(&client->dev, "Platform data is missing\n"); + if (!node) { + dev_err(&client->dev, "of node is missing\n"); return -EINVAL; } @@ -576,13 +570,10 @@ static int twl6040_probe(struct i2c_client *client, twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV); /* ERRATA: Automatic power-up is not possible in ES1.0 */ - if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) { - if (pdata) - twl6040->audpwron = pdata->audpwron_gpio; - else - twl6040->audpwron = of_get_named_gpio(node, - "ti,audpwron-gpio", 0); - } else + if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0) + twl6040->audpwron = of_get_named_gpio(node, + "ti,audpwron-gpio", 0); + else twl6040->audpwron = -EINVAL; if (gpio_is_valid(twl6040->audpwron)) { @@ -625,8 +616,6 @@ static int twl6040_probe(struct i2c_client *client, /* * The main functionality of twl6040 to provide audio on OMAP4+ systems. * We can add the ASoC codec child whenever this driver has been loaded. - * The ASoC codec can work without pdata, pass the platform_data only if - * it has been provided. */ irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_PLUG); cell = &twl6040->cells[children]; @@ -635,13 +624,10 @@ static int twl6040_probe(struct i2c_client *client, twl6040_codec_rsrc[0].end = irq; cell->resources = twl6040_codec_rsrc; cell->num_resources = ARRAY_SIZE(twl6040_codec_rsrc); - if (pdata && pdata->codec) { - cell->platform_data = pdata->codec; - cell->pdata_size = sizeof(*pdata->codec); - } children++; - if (twl6040_has_vibra(pdata, node)) { + /* Vibra input driver support */ + if (twl6040_has_vibra(node)) { irq = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_VIB); cell = &twl6040->cells[children]; @@ -650,28 +636,13 @@ static int twl6040_probe(struct i2c_client *client, twl6040_vibra_rsrc[0].end = irq; cell->resources = twl6040_vibra_rsrc; cell->num_resources = ARRAY_SIZE(twl6040_vibra_rsrc); - - if (pdata && pdata->vibra) { - cell->platform_data = pdata->vibra; - cell->pdata_size = sizeof(*pdata->vibra); - } children++; } - /* - * Enable the GPO driver in the following cases: - * DT booted kernel or legacy boot with valid gpo platform_data - */ - if (!pdata || (pdata && pdata->gpo)) { - cell = &twl6040->cells[children]; - cell->name = "twl6040-gpo"; - - if (pdata) { - cell->platform_data = pdata->gpo; - cell->pdata_size = sizeof(*pdata->gpo); - } - children++; - } + /* GPO support */ + cell = &twl6040->cells[children]; + cell->name = "twl6040-gpo"; + children++; ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children, NULL, 0, NULL); diff --git a/include/linux/mfd/twl6040.h b/include/linux/mfd/twl6040.h index 7e7fbce7a308..6dd8893b2a56 100644 --- a/include/linux/mfd/twl6040.h +++ b/include/linux/mfd/twl6040.h @@ -185,6 +185,7 @@ #define TWL6040_GPO_MAX 3 +/* TODO: All platform data struct can be removed */ struct twl6040_codec_data { u16 hs_left_step; u16 hs_right_step; -- cgit v1.2.3-71-gd317 From 08ad7b7f542bad5d1476573ddecefc1899da21e4 Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Sun, 7 Jul 2013 17:14:22 +0530 Subject: mfd: s2mps11: Remove clocks from regulators list Since these are fixed rate clocks which are registered with common clock framework so remove these from list of regulators which were unnecessarily incrementing the count(S2MPS11_REGULATOR_MAX) of regulators. Signed-off-by: Yadwinder Singh Brar Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- include/linux/mfd/samsung/s2mps11.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/samsung/s2mps11.h b/include/linux/mfd/samsung/s2mps11.h index 4e94dc65f987..179e10ca095b 100644 --- a/include/linux/mfd/samsung/s2mps11.h +++ b/include/linux/mfd/samsung/s2mps11.h @@ -167,11 +167,8 @@ enum s2mps11_regulators { S2MPS11_BUCK8, S2MPS11_BUCK9, S2MPS11_BUCK10, - S2MPS11_AP_EN32KHZ, - S2MPS11_CP_EN32KHZ, - S2MPS11_BT_EN32KHZ, - S2MPS11_REG_MAX, + S2MPS11_REGULATOR_MAX, }; #define S2MPS11_BUCK_MIN1 600000 @@ -192,6 +189,5 @@ enum s2mps11_regulators { #define S2MPS11_RAMP_DELAY 25000 /* uV/us */ #define S2MPS11_PMIC_EN_SHIFT 6 -#define S2MPS11_REGULATOR_MAX (S2MPS11_REG_MAX - 3) #endif /* __LINUX_MFD_S2MPS11_H */ -- cgit v1.2.3-71-gd317 From efe3126afce32e3100af3029a80701d47e1b6999 Mon Sep 17 00:00:00 2001 From: "Patil, Rachna" Date: Sat, 20 Jul 2013 17:27:35 +0100 Subject: MFD: ti_tscadc: ADC Clock check not required ADC is ideally expected to work at a frequency of 3MHz. The present code had a check, which returned error if the frequency went below the threshold value. But since AM335x supports various working frequencies, this check is not required. Now the code just uses the internal ADC clock divider to set the ADC frequency w.r.t the sys clock. Signed-off-by: Patil, Rachna Signed-off-by: Zubair Lutfullah Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/ti_am335x_tscadc.c | 6 +----- include/linux/mfd/ti_am335x_tscadc.h | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index b003a16ba227..e0852ecbc767 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -197,11 +197,7 @@ static int ti_tscadc_probe(struct platform_device *pdev) clock_rate = clk_get_rate(clk); clk_put(clk); clk_value = clock_rate / ADC_CLK; - if (clk_value < MAX_CLK_DIV) { - dev_err(&pdev->dev, "clock input less than min clock requirement\n"); - err = -EINVAL; - goto err_disable_clk; - } + /* TSCADC_CLKDIV needs to be configured to the value minus 1 */ clk_value = clk_value - 1; tscadc_writel(tscadc, REG_CLKDIV, clk_value); diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h index 8d73fe29796a..71bb41c32a64 100644 --- a/include/linux/mfd/ti_am335x_tscadc.h +++ b/include/linux/mfd/ti_am335x_tscadc.h @@ -114,7 +114,6 @@ #define CNTRLREG_TSCENB BIT(7) #define ADC_CLK 3000000 -#define MAX_CLK_DIV 7 #define TOTAL_STEPS 16 #define TOTAL_CHANNELS 8 -- cgit v1.2.3-71-gd317 From 62480dc8b4bd951057c0712c8547e6c3fa12008a Mon Sep 17 00:00:00 2001 From: Andrea Adami Date: Sun, 21 Jul 2013 01:07:42 +0200 Subject: mfd: mcp: Add missing linux/device.h header Fixes this compilation error: linux/include/linux/mfd/mcp.h:22:16: error: field 'attached_device' has incomplete type linux/include/linux/mfd/mcp.h:48:23: error: field 'drv' has incomplete type Signed-off-by: Andrea Adami Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- include/linux/mfd/mcp.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/mcp.h b/include/linux/mfd/mcp.h index a9e8bd157673..f682953043ba 100644 --- a/include/linux/mfd/mcp.h +++ b/include/linux/mfd/mcp.h @@ -10,6 +10,8 @@ #ifndef MCP_H #define MCP_H +#include + struct mcp_ops; struct mcp { -- cgit v1.2.3-71-gd317 From 38c4faaea4ca71c69584ef71cca8d0735d5361c1 Mon Sep 17 00:00:00 2001 From: Andrea Adami Date: Sun, 21 Jul 2013 01:07:39 +0200 Subject: mfd: ucb1x00: Explicitely include linux/device.h Fixes this compilation error: linux/include/linux/mfd/ucb1x00.h:137:17: error: field 'dev' has incomplete type Signed-off-by: Andrea Adami Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz --- include/linux/mfd/ucb1x00.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/mfd/ucb1x00.h b/include/linux/mfd/ucb1x00.h index 28af41756360..88f90cbf8e6a 100644 --- a/include/linux/mfd/ucb1x00.h +++ b/include/linux/mfd/ucb1x00.h @@ -10,6 +10,7 @@ #ifndef UCB1200_H #define UCB1200_H +#include #include #include #include -- cgit v1.2.3-71-gd317 From 66b52b0dc82c5c88d769dc1c7d44cf45d0deb07c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 29 Jul 2013 18:16:49 +0200 Subject: net: add ndo to get id of physical port of the device This patch adds a ndo for getting physical port of the device. Driver which is aware of being virtual function of some physical port should implement this ndo. This is applicable not only for IOV, but for other solutions (NPAR, multichannel) as well. Basically if there is possible to have multiple netdevs on the single hw port. Signed-off-by: Jiri Pirko Acked-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/netdevice.h | 20 ++++++++++++++++++++ net/core/dev.c | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 3ca60b070ef0..875f869dc38a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -728,6 +728,16 @@ struct netdev_fcoe_hbainfo { }; #endif +#define MAX_PHYS_PORT_ID_LEN 32 + +/* This structure holds a unique identifier to identify the + * physical port used by a netdevice. + */ +struct netdev_phys_port_id { + unsigned char id[MAX_PHYS_PORT_ID_LEN]; + unsigned char id_len; +}; + /* * This structure defines the management hooks for network devices. * The following hooks can be defined; unless noted otherwise, they are @@ -932,6 +942,12 @@ struct netdev_fcoe_hbainfo { * that determine carrier state from physical hardware properties (eg * network cables) or protocol-dependent mechanisms (eg * USB_CDC_NOTIFY_NETWORK_CONNECTION) should NOT implement this function. + * + * int (*ndo_get_phys_port_id)(struct net_device *dev, + * struct netdev_phys_port_id *ppid); + * Called to get ID of physical port of this device. If driver does + * not implement this, it is assumed that the hw is not able to have + * multiple net devices on single physical port. */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); @@ -1060,6 +1076,8 @@ struct net_device_ops { struct nlmsghdr *nlh); int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier); + int (*ndo_get_phys_port_id)(struct net_device *dev, + struct netdev_phys_port_id *ppid); }; /* @@ -2315,6 +2333,8 @@ extern int dev_set_mac_address(struct net_device *, struct sockaddr *); extern int dev_change_carrier(struct net_device *, bool new_carrier); +extern int dev_get_phys_port_id(struct net_device *dev, + struct netdev_phys_port_id *ppid); extern int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq); diff --git a/net/core/dev.c b/net/core/dev.c index dfd9f5d56ae0..58eb802584b9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4988,6 +4988,24 @@ int dev_change_carrier(struct net_device *dev, bool new_carrier) } EXPORT_SYMBOL(dev_change_carrier); +/** + * dev_get_phys_port_id - Get device physical port ID + * @dev: device + * @ppid: port ID + * + * Get device physical port ID + */ +int dev_get_phys_port_id(struct net_device *dev, + struct netdev_phys_port_id *ppid) +{ + const struct net_device_ops *ops = dev->netdev_ops; + + if (!ops->ndo_get_phys_port_id) + return -EOPNOTSUPP; + return ops->ndo_get_phys_port_id(dev, ppid); +} +EXPORT_SYMBOL(dev_get_phys_port_id); + /** * dev_new_index - allocate an ifindex * @net: the applicable net namespace -- cgit v1.2.3-71-gd317 From 66cae9ed6bc46b8cc57a9693f99f69926f3cc7ef Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 29 Jul 2013 18:16:50 +0200 Subject: rtnl: export physical port id via RT netlink Signed-off-by: Jiri Pirko Acked-by: Ben Hutchings Signed-off-by: Narendra K Signed-off-by: David S. Miller --- include/uapi/linux/if_link.h | 1 + net/core/rtnetlink.c | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 03f6170ab337..04c0e7a5d484 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -143,6 +143,7 @@ enum { IFLA_NUM_TX_QUEUES, IFLA_NUM_RX_QUEUES, IFLA_CARRIER, + IFLA_PHYS_PORT_ID, __IFLA_MAX }; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 3de740834d1f..0b2972c59b97 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -767,7 +767,8 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */ + rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */ + rtnl_link_get_size(dev) /* IFLA_LINKINFO */ - + rtnl_link_get_af_size(dev); /* IFLA_AF_SPEC */ + + rtnl_link_get_af_size(dev) /* IFLA_AF_SPEC */ + + nla_total_size(MAX_PHYS_PORT_ID_LEN); /* IFLA_PHYS_PORT_ID */ } static int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev) @@ -846,6 +847,24 @@ static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev) return 0; } +static int rtnl_phys_port_id_fill(struct sk_buff *skb, struct net_device *dev) +{ + int err; + struct netdev_phys_port_id ppid; + + err = dev_get_phys_port_id(dev, &ppid); + if (err) { + if (err == -EOPNOTSUPP) + return 0; + return err; + } + + if (nla_put(skb, IFLA_PHYS_PORT_ID, ppid.id_len, ppid.id)) + return -EMSGSIZE; + + return 0; +} + static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, int type, u32 pid, u32 seq, u32 change, unsigned int flags, u32 ext_filter_mask) @@ -913,6 +932,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, goto nla_put_failure; } + if (rtnl_phys_port_id_fill(skb, dev)) + goto nla_put_failure; + attr = nla_reserve(skb, IFLA_STATS, sizeof(struct rtnl_link_stats)); if (attr == NULL) @@ -1113,6 +1135,7 @@ const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_PROMISCUITY] = { .type = NLA_U32 }, [IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 }, [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, + [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_PORT_ID_LEN }, }; EXPORT_SYMBOL(ifla_policy); -- cgit v1.2.3-71-gd317 From 59da381ee2afc806f85becf3aa64ffc952355552 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 31 Jul 2013 06:53:21 +0000 Subject: PCI: move enum pcie_link_width into pci.h pcie_link_width is the enum used to define the link width values for a pcie device. This enum should not be contained solely in pci_hotplug.h, and this patch moves it next to pci_bus_speed in pci.h Signed-off-by: Jacob Keller Tested-by: Phil Schmitt Acked-by: Bjorn Helgaas Signed-off-by: Jeff Kirsher --- include/linux/pci.h | 13 +++++++++++++ include/linux/pci_hotplug.h | 13 ------------- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/pci.h b/include/linux/pci.h index 0fd1f1582fa1..a0bf22d816cf 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -183,6 +183,19 @@ enum pci_bus_flags { PCI_BUS_FLAGS_NO_MMRBC = (__force pci_bus_flags_t) 2, }; +/* These values come from the PCI Express Spec */ +enum pcie_link_width { + PCIE_LNK_WIDTH_RESRV = 0x00, + PCIE_LNK_X1 = 0x01, + PCIE_LNK_X2 = 0x02, + PCIE_LNK_X4 = 0x04, + PCIE_LNK_X8 = 0x08, + PCIE_LNK_X12 = 0x0C, + PCIE_LNK_X16 = 0x10, + PCIE_LNK_X32 = 0x20, + PCIE_LNK_WIDTH_UNKNOWN = 0xFF, +}; + /* Based on the PCI Hotplug Spec, but some values are made up by us */ enum pci_bus_speed { PCI_SPEED_33MHz = 0x00, diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 8db71dcd6337..64e61e05d9a0 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -28,19 +28,6 @@ #ifndef _PCI_HOTPLUG_H #define _PCI_HOTPLUG_H -/* These values come from the PCI Express Spec */ -enum pcie_link_width { - PCIE_LNK_WIDTH_RESRV = 0x00, - PCIE_LNK_X1 = 0x01, - PCIE_LNK_X2 = 0x02, - PCIE_LNK_X4 = 0x04, - PCIE_LNK_X8 = 0x08, - PCIE_LNK_X12 = 0x0C, - PCIE_LNK_X16 = 0x10, - PCIE_LNK_X32 = 0x20, - PCIE_LNK_WIDTH_UNKNOWN = 0xFF, -}; - /** * struct hotplug_slot_ops -the callbacks that the hotplug pci core can use * @owner: The module owner of this structure -- cgit v1.2.3-71-gd317 From 81377c8d3563e7aec5c8baaaacacb48034f430a0 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Wed, 31 Jul 2013 06:53:26 +0000 Subject: PCI: Add function to obtain minimum link width and speed A PCI Express device can potentially report a link width and speed which it will not properly fulfill due to being plugged into a slower link higher in the chain. This function walks up the PCI bus chain and calculates the minimum link width and speed of this entire chain. This can be useful to enable a device to determine if it has enough bandwidth for optimum functionality. Signed-off-by: Jacob Keller Tested-by: Phil Schmitt Acked-by: Bjorn Helgaas Signed-off-by: Jeff Kirsher --- drivers/pci/pci.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 2 ++ 2 files changed, 45 insertions(+) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e37fea6e178d..c71e78c46705 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3578,6 +3578,49 @@ int pcie_set_mps(struct pci_dev *dev, int mps) PCI_EXP_DEVCTL_PAYLOAD, v); } +/** + * pcie_get_minimum_link - determine minimum link settings of a PCI device + * @dev: PCI device to query + * @speed: storage for minimum speed + * @width: storage for minimum width + * + * This function will walk up the PCI device chain and determine the minimum + * link width and speed of the device. + */ +int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, + enum pcie_link_width *width) +{ + int ret; + + *speed = PCI_SPEED_UNKNOWN; + *width = PCIE_LNK_WIDTH_UNKNOWN; + + while (dev) { + u16 lnksta; + enum pci_bus_speed next_speed; + enum pcie_link_width next_width; + + ret = pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + if (ret) + return ret; + + next_speed = pcie_link_speed[lnksta & PCI_EXP_LNKSTA_CLS]; + next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> + PCI_EXP_LNKSTA_NLW_SHIFT; + + if (next_speed < *speed) + *speed = next_speed; + + if (next_width < *width) + *width = next_width; + + dev = dev->bus->self; + } + + return 0; +} +EXPORT_SYMBOL(pcie_get_minimum_link); + /** * pci_select_bars - Make BAR mask from the type of resource * @dev: the PCI device for which BAR mask is made diff --git a/include/linux/pci.h b/include/linux/pci.h index a0bf22d816cf..2edbee64aeba 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -934,6 +934,8 @@ int pcie_get_readrq(struct pci_dev *dev); int pcie_set_readrq(struct pci_dev *dev, int rq); int pcie_get_mps(struct pci_dev *dev); int pcie_set_mps(struct pci_dev *dev, int mps); +int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, + enum pcie_link_width *width); int __pci_reset_function(struct pci_dev *dev); int __pci_reset_function_locked(struct pci_dev *dev); int pci_reset_function(struct pci_dev *dev); -- cgit v1.2.3-71-gd317 From ddf64a3c03d4d68431146a0f1622844cc6cb6c22 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 15 Jul 2013 19:10:10 +0200 Subject: HID: usbhid: make usbhid_set_leds() static usbhid_set_leds() is only used inside of usbhid/hid-core.c so no need to export it. Signed-off-by: David Herrmann Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 3 +-- include/linux/hid.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 99418285222c..5482bf447688 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -857,7 +857,7 @@ static int hid_find_field_early(struct hid_device *hid, unsigned int page, return -1; } -void usbhid_set_leds(struct hid_device *hid) +static void usbhid_set_leds(struct hid_device *hid) { struct hid_field *field; int offset; @@ -867,7 +867,6 @@ void usbhid_set_leds(struct hid_device *hid) usbhid_submit_report(hid, field->report, USB_DIR_OUT); } } -EXPORT_SYMBOL_GPL(usbhid_set_leds); /* * Traverse the supplied list of reports and find the longest diff --git a/include/linux/hid.h b/include/linux/hid.h index 0c48991b0402..b8058c5c5594 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -989,7 +989,6 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct); int usbhid_quirks_init(char **quirks_param); void usbhid_quirks_exit(void); -void usbhid_set_leds(struct hid_device *hid); #ifdef CONFIG_HID_PID int hid_pidff_init(struct hid_device *hid); -- cgit v1.2.3-71-gd317 From bdb829e1dd710029a075b5f86d4053e7715beb06 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 15 Jul 2013 19:10:15 +0200 Subject: HID: uhid: use generic hidinput_input_event() HID core provides the same functionality and can convert the input event to a raw output report. We can thus drop UHID_OUTPUT_EV and rely on the mandatory UHID_OUTPUT. User-space wasn't able to do anything with UHID_OUTPUT_EV, anyway. They don't have access to the report fields. Signed-off-by: David Herrmann Acked-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- Documentation/hid/uhid.txt | 4 +++- drivers/hid/uhid.c | 25 ------------------------- include/uapi/linux/uhid.h | 4 +++- 3 files changed, 6 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/Documentation/hid/uhid.txt b/Documentation/hid/uhid.txt index 3c741214dfbb..dc35a2b75eee 100644 --- a/Documentation/hid/uhid.txt +++ b/Documentation/hid/uhid.txt @@ -149,11 +149,13 @@ needs. Only UHID_OUTPUT and UHID_OUTPUT_EV have payloads. is of type "struct uhid_data_req". This may be received even though you haven't received UHID_OPEN, yet. - UHID_OUTPUT_EV: + UHID_OUTPUT_EV (obsolete): Same as UHID_OUTPUT but this contains a "struct input_event" as payload. This is called for force-feedback, LED or similar events which are received through an input device by the HID subsystem. You should convert this into raw reports and send them to your device similar to events of type UHID_OUTPUT. + This is no longer sent by newer kernels. Instead, HID core converts it into a + raw output report and sends it via UHID_OUTPUT. UHID_FEATURE: This event is sent if the kernel driver wants to perform a feature request as diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index fc307e0422af..f53f2d52e677 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -116,30 +116,6 @@ static void uhid_hid_close(struct hid_device *hid) uhid_queue_event(uhid, UHID_CLOSE); } -static int uhid_hid_input(struct input_dev *input, unsigned int type, - unsigned int code, int value) -{ - struct hid_device *hid = input_get_drvdata(input); - struct uhid_device *uhid = hid->driver_data; - unsigned long flags; - struct uhid_event *ev; - - ev = kzalloc(sizeof(*ev), GFP_ATOMIC); - if (!ev) - return -ENOMEM; - - ev->type = UHID_OUTPUT_EV; - ev->u.output_ev.type = type; - ev->u.output_ev.code = code; - ev->u.output_ev.value = value; - - spin_lock_irqsave(&uhid->qlock, flags); - uhid_queue(uhid, ev); - spin_unlock_irqrestore(&uhid->qlock, flags); - - return 0; -} - static int uhid_hid_parse(struct hid_device *hid) { struct uhid_device *uhid = hid->driver_data; @@ -273,7 +249,6 @@ static struct hid_ll_driver uhid_hid_driver = { .stop = uhid_hid_stop, .open = uhid_hid_open, .close = uhid_hid_close, - .hidinput_input_event = uhid_hid_input, .parse = uhid_hid_parse, }; diff --git a/include/uapi/linux/uhid.h b/include/uapi/linux/uhid.h index e9ed951e2b09..414b74be4da1 100644 --- a/include/uapi/linux/uhid.h +++ b/include/uapi/linux/uhid.h @@ -30,7 +30,7 @@ enum uhid_event_type { UHID_OPEN, UHID_CLOSE, UHID_OUTPUT, - UHID_OUTPUT_EV, + UHID_OUTPUT_EV, /* obsolete! */ UHID_INPUT, UHID_FEATURE, UHID_FEATURE_ANSWER, @@ -69,6 +69,8 @@ struct uhid_output_req { __u8 rtype; } __attribute__((__packed__)); +/* Obsolete! Newer kernels will no longer send these events but instead convert + * it into raw output reports via UHID_OUTPUT. */ struct uhid_output_ev_req { __u16 type; __u16 code; -- cgit v1.2.3-71-gd317 From 4bdfb2729c3a396fe7400c9332c49aee2b971bd8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 29 Jul 2013 21:00:53 +0100 Subject: regulator: core: Add missing stub for regulator_get_exclusive() Signed-off-by: Mark Brown --- include/linux/regulator/consumer.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 3a76389c6aaa..60da4a62c402 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -211,6 +211,13 @@ static inline struct regulator *__must_check regulator_get(struct device *dev, return NULL; } +static inline struct regulator *__must_check +regulator_get_exclusive(struct device *dev, const char *id) +{ + return NULL; +} + + static inline struct regulator *__must_check devm_regulator_get(struct device *dev, const char *id) { -- cgit v1.2.3-71-gd317 From de1dd9fd2156874b45803299b3b27e65d5defdd9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 29 Jul 2013 21:42:42 +0100 Subject: regulator: core: Provide hints to the core about optional supplies While the majority of supplies on devices are mandatory and can't be physically omitted for electrical reasons some devices do have optional supplies and need to know if they are missing, MMC being the most common of these. Currently the core accurately reports all errors when regulators are requested since it does not know if the supply is one that must be provided even if by a regulator software does not know about or if it is one that may genuinely be disconnected. In order to allow this behaviour to be changed and stub regulators to be provided in the former case add a new regulator request function regulator_get_optional() which provides a hint to the core that the regulator may genuinely not be connected. Currently the implementation is identical to the current behaviour, future patches will add support in the core for returning stub regulators in the case where normal regulator_get() fails and the board has requested it. Signed-off-by: Mark Brown Acked-by: Chris Ball --- drivers/regulator/core.c | 59 ++++++++++++++++++++++++++++++++++++++ include/linux/regulator/consumer.h | 18 +++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 288c75abc190..a27a5b6267dd 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1409,6 +1409,65 @@ struct regulator *regulator_get_exclusive(struct device *dev, const char *id) } EXPORT_SYMBOL_GPL(regulator_get_exclusive); +/** + * regulator_get_optional - obtain optional access to a regulator. + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Returns a struct regulator corresponding to the regulator producer, + * or IS_ERR() condition containing errno. Other consumers will be + * unable to obtain this reference is held and the use count for the + * regulator will be initialised to reflect the current state of the + * regulator. + * + * This is intended for use by consumers for devices which can have + * some supplies unconnected in normal use, such as some MMC devices. + * It can allow the regulator core to provide stub supplies for other + * supplies requested using normal regulator_get() calls without + * disrupting the operation of drivers that can handle absent + * supplies. + * + * Use of supply names configured via regulator_set_device_supply() is + * strongly encouraged. It is recommended that the supply name used + * should match the name used for the supply and/or the relevant + * device pins in the datasheet. + */ +struct regulator *regulator_get_optional(struct device *dev, const char *id) +{ + return _regulator_get(dev, id, 0); +} +EXPORT_SYMBOL_GPL(regulator_get_optional); + +/** + * devm_regulator_get_optional - Resource managed regulator_get_optional() + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Managed regulator_get_optional(). Regulators returned from this + * function are automatically regulator_put() on driver detach. See + * regulator_get_optional() for more information. + */ +struct regulator *devm_regulator_get_optional(struct device *dev, + const char *id) +{ + struct regulator **ptr, *regulator; + + ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + regulator = regulator_get_optional(dev, id); + if (!IS_ERR(regulator)) { + *ptr = regulator; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return regulator; +} +EXPORT_SYMBOL_GPL(devm_regulator_get_optional); + /* Locks held by regulator_put() */ static void _regulator_put(struct regulator *regulator) { diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 60da4a62c402..e2bac6db4abc 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -137,6 +137,10 @@ struct regulator *__must_check devm_regulator_get(struct device *dev, const char *id); struct regulator *__must_check regulator_get_exclusive(struct device *dev, const char *id); +struct regulator *__must_check regulator_get_optional(struct device *dev, + const char *id); +struct regulator *__must_check devm_regulator_get_optional(struct device *dev, + const char *id); void regulator_put(struct regulator *regulator); void devm_regulator_put(struct regulator *regulator); @@ -211,15 +215,27 @@ static inline struct regulator *__must_check regulator_get(struct device *dev, return NULL; } +static inline struct regulator *__must_check +devm_regulator_get(struct device *dev, const char *id) +{ + return NULL; +} + static inline struct regulator *__must_check regulator_get_exclusive(struct device *dev, const char *id) { return NULL; } +static inline struct regulator *__must_check +regulator_get_optional(struct device *dev, const char *id) +{ + return NULL; +} + static inline struct regulator *__must_check -devm_regulator_get(struct device *dev, const char *id) +devm_regulator_get_optional(struct device *dev, const char *id) { return NULL; } -- cgit v1.2.3-71-gd317 From 50c9d75b6f01a337aab728511bc1d2a0a3d7b800 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 15 Jul 2013 19:10:12 +0200 Subject: HID: input: generic hidinput_input_event handler The hidinput_input_event() callback converts input events written from userspace into HID reports and sends them to the device. We currently implement this in every HID transport driver, even though most of them do the same. This provides a generic hidinput_input_event() implementation which is mostly copied from usbhid. It uses a delayed worker to allow multiple LED events to be collected into a single output event. We use the custom ->request() transport driver callback to allow drivers to adjust the outgoing report and handle the request asynchronously. If no custom ->request() callback is available, we fall back to the generic raw output report handler (which is synchronous). Drivers can still provide custom hidinput_input_event() handlers (see logitech-dj) if the generic implementation doesn't fit their needs. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/hid.h | 1 + 2 files changed, 80 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7480799e535c..308eee8fc7c3 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1137,6 +1137,74 @@ unsigned int hidinput_count_leds(struct hid_device *hid) } EXPORT_SYMBOL_GPL(hidinput_count_leds); +static void hidinput_led_worker(struct work_struct *work) +{ + struct hid_device *hid = container_of(work, struct hid_device, + led_work); + struct hid_field *field; + struct hid_report *report; + int len; + __u8 *buf; + + field = hidinput_get_led_field(hid); + if (!field) + return; + + /* + * field->report is accessed unlocked regarding HID core. So there might + * be another incoming SET-LED request from user-space, which changes + * the LED state while we assemble our outgoing buffer. However, this + * doesn't matter as hid_output_report() correctly converts it into a + * boolean value no matter what information is currently set on the LED + * field (even garbage). So the remote device will always get a valid + * request. + * And in case we send a wrong value, a next led worker is spawned + * for every SET-LED request so the following worker will send the + * correct value, guaranteed! + */ + + report = field->report; + + /* use custom SET_REPORT request if possible (asynchronous) */ + if (hid->ll_driver->request) + return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT); + + /* fall back to generic raw-output-report */ + len = ((report->size - 1) >> 3) + 1 + (report->id > 0); + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return; + + hid_output_report(report, buf); + /* synchronous output report */ + hid->hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT); + kfree(buf); +} + +static int hidinput_input_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct hid_field *field; + int offset; + + if (type == EV_FF) + return input_ff_event(dev, type, code, value); + + if (type != EV_LED) + return -1; + + if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) { + hid_warn(dev, "event field not found\n"); + return -1; + } + + hid_set_field(field, offset, value); + + schedule_work(&hid->led_work); + return 0; +} + static int hidinput_open(struct input_dev *dev) { struct hid_device *hid = input_get_drvdata(dev); @@ -1183,7 +1251,10 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid) } input_set_drvdata(input_dev, hid); - input_dev->event = hid->ll_driver->hidinput_input_event; + if (hid->ll_driver->hidinput_input_event) + input_dev->event = hid->ll_driver->hidinput_input_event; + else if (hid->ll_driver->request || hid->hid_output_raw_report) + input_dev->event = hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; input_dev->setkeycode = hidinput_setkeycode; @@ -1278,6 +1349,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) int i, j, k; INIT_LIST_HEAD(&hid->inputs); + INIT_WORK(&hid->led_work, hidinput_led_worker); if (!force) { for (i = 0; i < hid->maxcollection; i++) { @@ -1379,6 +1451,12 @@ void hidinput_disconnect(struct hid_device *hid) input_unregister_device(hidinput->input); kfree(hidinput); } + + /* led_work is spawned by input_dev callbacks, but doesn't access the + * parent input_dev at all. Once all input devices are removed, we + * know that led_work will never get restarted, so we can cancel it + * synchronously and are safe. */ + cancel_work_sync(&hid->led_work); } EXPORT_SYMBOL_GPL(hidinput_disconnect); diff --git a/include/linux/hid.h b/include/linux/hid.h index b8058c5c5594..ea4b828cb9cd 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -456,6 +456,7 @@ struct hid_device { /* device report descriptor */ enum hid_type type; /* device type (mouse, kbd, ...) */ unsigned country; /* HID country */ struct hid_report_enum report_enum[HID_REPORT_TYPES]; + struct work_struct led_work; /* delayed LED worker */ struct semaphore driver_lock; /* protects the current driver, except during input */ struct semaphore driver_input_lock; /* protects the current driver */ -- cgit v1.2.3-71-gd317 From 3d7d248cf484fe37595698e0ca31a9bcecc85a42 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 13 Jun 2013 09:50:35 +0200 Subject: HID: i2c-hid: add DT bindings Add device tree based support for HID over I2C devices. Tested on an Odroid-X board with a Synaptics touchpad. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- .../devicetree/bindings/hid/hid-over-i2c.txt | 28 ++++++++++++++ drivers/hid/i2c-hid/i2c-hid.c | 44 +++++++++++++++++++++- include/linux/i2c/i2c-hid.h | 3 +- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/hid/hid-over-i2c.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/hid/hid-over-i2c.txt b/Documentation/devicetree/bindings/hid/hid-over-i2c.txt new file mode 100644 index 000000000000..488edcb264c4 --- /dev/null +++ b/Documentation/devicetree/bindings/hid/hid-over-i2c.txt @@ -0,0 +1,28 @@ +* HID over I2C Device-Tree bindings + +HID over I2C provides support for various Human Interface Devices over the +I2C bus. These devices can be for example touchpads, keyboards, touch screens +or sensors. + +The specification has been written by Microsoft and is currently available here: +http://msdn.microsoft.com/en-us/library/windows/hardware/hh852380.aspx + +If this binding is used, the kernel module i2c-hid will handle the communication +with the device and the generic hid core layer will handle the protocol. + +Required properties: +- compatible: must be "hid-over-i2c" +- reg: i2c slave address +- hid-descr-addr: HID descriptor address +- interrupt-parent: the phandle for the interrupt controller +- interrupts: interrupt line + +Example: + + i2c-hid-dev@2c { + compatible = "hid-over-i2c"; + reg = <0x2c>; + hid-descr-addr = <0x0020>; + interrupt-parent = <&gpx3>; + interrupts = <3 2>; + }; diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 879b0ed701a3..fc9d92cb3f39 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -933,6 +934,42 @@ static inline int i2c_hid_acpi_pdata(struct i2c_client *client, } #endif +#ifdef CONFIG_OF +static int i2c_hid_of_probe(struct i2c_client *client, + struct i2c_hid_platform_data *pdata) +{ + struct device *dev = &client->dev; + u32 val; + int ret; + + ret = of_property_read_u32(dev->of_node, "hid-descr-addr", &val); + if (ret) { + dev_err(&client->dev, "HID register address not provided\n"); + return -ENODEV; + } + if (val >> 16) { + dev_err(&client->dev, "Bad HID register address: 0x%08x\n", + val); + return -EINVAL; + } + pdata->hid_descriptor_address = val; + + return 0; +} + +static const struct of_device_id i2c_hid_of_match[] = { + { .compatible = "hid-over-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_hid_of_match); +#else +static inline int i2c_hid_of_probe(struct i2c_client *client, + struct i2c_hid_platform_data *pdata) +{ + return -ENODEV; +} +#endif + static int i2c_hid_probe(struct i2c_client *client, const struct i2c_device_id *dev_id) { @@ -954,7 +991,11 @@ static int i2c_hid_probe(struct i2c_client *client, if (!ihid) return -ENOMEM; - if (!platform_data) { + if (client->dev.of_node) { + ret = i2c_hid_of_probe(client, &ihid->pdata); + if (ret) + goto err; + } else if (!platform_data) { ret = i2c_hid_acpi_pdata(client, &ihid->pdata); if (ret) { dev_err(&client->dev, @@ -1095,6 +1136,7 @@ static struct i2c_driver i2c_hid_driver = { .owner = THIS_MODULE, .pm = &i2c_hid_pm, .acpi_match_table = ACPI_PTR(i2c_hid_acpi_match), + .of_match_table = of_match_ptr(i2c_hid_of_match), }, .probe = i2c_hid_probe, diff --git a/include/linux/i2c/i2c-hid.h b/include/linux/i2c/i2c-hid.h index 60e411d764d4..7aa901d92058 100644 --- a/include/linux/i2c/i2c-hid.h +++ b/include/linux/i2c/i2c-hid.h @@ -19,7 +19,8 @@ * @hid_descriptor_address: i2c register where the HID descriptor is stored. * * Note that it is the responsibility of the platform driver (or the acpi 5.0 - * driver) to setup the irq related to the gpio in the struct i2c_board_info. + * driver, or the flattened device tree) to setup the irq related to the gpio in + * the struct i2c_board_info. * The platform driver should also setup the gpio according to the device: * * A typical example is the following: -- cgit v1.2.3-71-gd317 From 6f4b7e632d78c2d91502211c430722cc66428492 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 31 Jul 2013 16:18:36 +0800 Subject: cgroup: more naming cleanups Constantly use @cset for css_set variables and use @cgrp as cgroup variables. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 6 +++--- kernel/cgroup.c | 26 +++++++++++++------------- kernel/cpuset.c | 16 ++++++++-------- 3 files changed, 24 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 297462b9f41a..00a7e07a1567 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -394,8 +394,8 @@ struct cgroup_map_cb { /* cftype->flags */ enum { - CFTYPE_ONLY_ON_ROOT = (1 << 0), /* only create on root cg */ - CFTYPE_NOT_ON_ROOT = (1 << 1), /* don't create on root cg */ + CFTYPE_ONLY_ON_ROOT = (1 << 0), /* only create on root cgrp */ + CFTYPE_NOT_ON_ROOT = (1 << 1), /* don't create on root cgrp */ CFTYPE_INSANE = (1 << 2), /* don't create if sane_behavior */ }; @@ -513,7 +513,7 @@ struct cftype_set { }; struct cgroup_scanner { - struct cgroup *cg; + struct cgroup *cgrp; int (*test_task)(struct task_struct *p, struct cgroup_scanner *scan); void (*process_task)(struct task_struct *p, struct cgroup_scanner *scan); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index ed2104304833..9577bebe2546 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -466,7 +466,7 @@ static inline void put_css_set_taskexit(struct css_set *cset) * @new_cgrp: cgroup that's being entered by the task * @template: desired set of css pointers in css_set (pre-calculated) * - * Returns true if "cg" matches "old_cg" except for the hierarchy + * Returns true if "cset" matches "old_cset" except for the hierarchy * which "new_cgrp" belongs to, for which it should match "new_cgrp". */ static bool compare_css_sets(struct css_set *cset, @@ -1839,7 +1839,7 @@ EXPORT_SYMBOL_GPL(task_cgroup_path_from_hierarchy); struct task_and_cgroup { struct task_struct *task; struct cgroup *cgrp; - struct css_set *cg; + struct css_set *cset; }; struct cgroup_taskset { @@ -2057,8 +2057,8 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk, tc = flex_array_get(group, i); old_cset = task_css_set(tc->task); - tc->cg = find_css_set(old_cset, cgrp); - if (!tc->cg) { + tc->cset = find_css_set(old_cset, cgrp); + if (!tc->cset) { retval = -ENOMEM; goto out_put_css_set_refs; } @@ -2071,7 +2071,7 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk, */ for (i = 0; i < group_size; i++) { tc = flex_array_get(group, i); - cgroup_task_migrate(tc->cgrp, tc->task, tc->cg); + cgroup_task_migrate(tc->cgrp, tc->task, tc->cset); } /* nothing is sensitive to fork() after this point. */ @@ -2091,9 +2091,9 @@ out_put_css_set_refs: if (retval) { for (i = 0; i < group_size; i++) { tc = flex_array_get(group, i); - if (!tc->cg) + if (!tc->cset) break; - put_css_set(tc->cg); + put_css_set(tc->cset); } } out_cancel_attach: @@ -2203,9 +2203,9 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk) mutex_lock(&cgroup_mutex); for_each_active_root(root) { - struct cgroup *from_cg = task_cgroup_from_root(from, root); + struct cgroup *from_cgrp = task_cgroup_from_root(from, root); - retval = cgroup_attach_task(from_cg, tsk, false); + retval = cgroup_attach_task(from_cgrp, tsk, false); if (retval) break; } @@ -3305,8 +3305,8 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan) * guarantees forward progress and that we don't miss any tasks. */ heap->size = 0; - cgroup_iter_start(scan->cg, &it); - while ((p = cgroup_iter_next(scan->cg, &it))) { + cgroup_iter_start(scan->cgrp, &it); + while ((p = cgroup_iter_next(scan->cgrp, &it))) { /* * Only affect tasks that qualify per the caller's callback, * if he provided one @@ -3339,7 +3339,7 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan) * the heap and wasn't inserted */ } - cgroup_iter_end(scan->cg, &it); + cgroup_iter_end(scan->cgrp, &it); if (heap->size) { for (i = 0; i < heap->size; i++) { @@ -3385,7 +3385,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) { struct cgroup_scanner scan; - scan.cg = from; + scan.cgrp = from; scan.test_task = NULL; /* select all tasks in cgroup */ scan.process_task = cgroup_transfer_one_task; scan.heap = NULL; diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 703bfd5a32a9..1b9c31549797 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -845,7 +845,7 @@ static void cpuset_change_cpumask(struct task_struct *tsk, { struct cpuset *cpus_cs; - cpus_cs = effective_cpumask_cpuset(cgroup_cs(scan->cg)); + cpus_cs = effective_cpumask_cpuset(cgroup_cs(scan->cgrp)); set_cpus_allowed_ptr(tsk, cpus_cs->cpus_allowed); } @@ -866,7 +866,7 @@ static void update_tasks_cpumask(struct cpuset *cs, struct ptr_heap *heap) { struct cgroup_scanner scan; - scan.cg = cs->css.cgroup; + scan.cgrp = cs->css.cgroup; scan.test_task = NULL; scan.process_task = cpuset_change_cpumask; scan.heap = heap; @@ -1062,7 +1062,7 @@ static void cpuset_change_task_nodemask(struct task_struct *tsk, static void cpuset_change_nodemask(struct task_struct *p, struct cgroup_scanner *scan) { - struct cpuset *cs = cgroup_cs(scan->cg); + struct cpuset *cs = cgroup_cs(scan->cgrp); struct mm_struct *mm; int migrate; nodemask_t *newmems = scan->data; @@ -1102,7 +1102,7 @@ static void update_tasks_nodemask(struct cpuset *cs, struct ptr_heap *heap) guarantee_online_mems(mems_cs, &newmems); - scan.cg = cs->css.cgroup; + scan.cgrp = cs->css.cgroup; scan.test_task = NULL; scan.process_task = cpuset_change_nodemask; scan.heap = heap; @@ -1275,7 +1275,7 @@ static int update_relax_domain_level(struct cpuset *cs, s64 val) static void cpuset_change_flag(struct task_struct *tsk, struct cgroup_scanner *scan) { - cpuset_update_task_spread_flag(cgroup_cs(scan->cg), tsk); + cpuset_update_task_spread_flag(cgroup_cs(scan->cgrp), tsk); } /* @@ -1295,7 +1295,7 @@ static void update_tasks_flags(struct cpuset *cs, struct ptr_heap *heap) { struct cgroup_scanner scan; - scan.cg = cs->css.cgroup; + scan.cgrp = cs->css.cgroup; scan.test_task = NULL; scan.process_task = cpuset_change_flag; scan.heap = heap; @@ -1971,7 +1971,7 @@ static int cpuset_css_online(struct cgroup *cgrp) struct cpuset *cs = cgroup_cs(cgrp); struct cpuset *parent = parent_cs(cs); struct cpuset *tmp_cs; - struct cgroup *pos_cg; + struct cgroup *pos_cgrp; if (!parent) return 0; @@ -2003,7 +2003,7 @@ static int cpuset_css_online(struct cgroup *cgrp) * (and likewise for mems) to the new cgroup. */ rcu_read_lock(); - cpuset_for_each_child(tmp_cs, pos_cg, parent) { + cpuset_for_each_child(tmp_cs, pos_cgrp, parent) { if (is_mem_exclusive(tmp_cs) || is_cpu_exclusive(tmp_cs)) { rcu_read_unlock(); goto out_unlock; -- cgit v1.2.3-71-gd317 From 4e96ee8e981b5140a2bcc5fff0d5c0eef39a62ee Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 31 Jul 2013 09:50:50 +0800 Subject: cgroup: convert cgroup_ida to cgroup_idr This enables us to lookup a cgroup by its id. v4: - add a comment for idr_remove() in cgroup_offline_fn(). v3: - on success, idr_alloc() returns the id but not 0, so fix the BUG_ON() in cgroup_init(). - pass the right value to idr_alloc() so that the id for dummy cgroup is 0. Signed-off-by: Li Zefan Reviewed-by: Michal Hocko Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 4 ++-- kernel/cgroup.c | 33 +++++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 00a7e07a1567..cca570e188fb 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -161,7 +161,7 @@ struct cgroup_name { struct cgroup { unsigned long flags; /* "unsigned long" so bitops work */ - int id; /* ida allocated in-hierarchy ID */ + int id; /* idr allocated in-hierarchy ID */ /* * We link our 'sibling' struct into our parent's 'children'. @@ -322,7 +322,7 @@ struct cgroupfs_root { unsigned long flags; /* IDs for cgroups in this hierarchy */ - struct ida cgroup_ida; + struct idr cgroup_idr; /* The path to use for release notifications. */ char release_agent_path[PATH_MAX]; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 9577bebe2546..3f6593333525 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -866,8 +866,6 @@ static void cgroup_free_fn(struct work_struct *work) */ dput(cgrp->parent->dentry); - ida_simple_remove(&cgrp->root->cgroup_ida, cgrp->id); - /* * Drop the active superblock reference that we took when we * created the cgroup. This will free cgrp->root, if we are @@ -1379,6 +1377,7 @@ static void init_cgroup_root(struct cgroupfs_root *root) cgrp->root = root; RCU_INIT_POINTER(cgrp->name, &root_cgroup_name); init_cgroup_housekeeping(cgrp); + idr_init(&root->cgroup_idr); } static int cgroup_init_root_id(struct cgroupfs_root *root, int start, int end) @@ -1451,7 +1450,6 @@ static struct cgroupfs_root *cgroup_root_from_opts(struct cgroup_sb_opts *opts) */ root->subsys_mask = opts->subsys_mask; root->flags = opts->flags; - ida_init(&root->cgroup_ida); if (opts->release_agent) strcpy(root->release_agent_path, opts->release_agent); if (opts->name) @@ -1467,7 +1465,7 @@ static void cgroup_free_root(struct cgroupfs_root *root) /* hierarhcy ID shoulid already have been released */ WARN_ON_ONCE(root->hierarchy_id); - ida_destroy(&root->cgroup_ida); + idr_destroy(&root->cgroup_idr); kfree(root); } } @@ -1582,6 +1580,11 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type, mutex_lock(&cgroup_mutex); mutex_lock(&cgroup_root_mutex); + root_cgrp->id = idr_alloc(&root->cgroup_idr, root_cgrp, + 0, 1, GFP_KERNEL); + if (root_cgrp->id < 0) + goto unlock_drop; + /* Check for name clashes with existing mounts */ ret = -EBUSY; if (strlen(root->name)) @@ -4253,7 +4256,11 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, goto err_free_cgrp; rcu_assign_pointer(cgrp->name, name); - cgrp->id = ida_simple_get(&root->cgroup_ida, 1, 0, GFP_KERNEL); + /* + * Temporarily set the pointer to NULL, so idr_find() won't return + * a half-baked cgroup. + */ + cgrp->id = idr_alloc(&root->cgroup_idr, NULL, 1, 0, GFP_KERNEL); if (cgrp->id < 0) goto err_free_name; @@ -4351,6 +4358,8 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, } } + idr_replace(&root->cgroup_idr, cgrp, cgrp->id); + err = cgroup_addrm_files(cgrp, NULL, cgroup_base_files, true); if (err) goto err_destroy; @@ -4377,7 +4386,7 @@ err_free_all: /* Release the reference count that we took on the superblock */ deactivate_super(sb); err_free_id: - ida_simple_remove(&root->cgroup_ida, cgrp->id); + idr_remove(&root->cgroup_idr, cgrp->id); err_free_name: kfree(rcu_dereference_raw(cgrp->name)); err_free_cgrp: @@ -4570,6 +4579,14 @@ static void cgroup_offline_fn(struct work_struct *work) /* delete this cgroup from parent->children */ list_del_rcu(&cgrp->sibling); + /* + * We should remove the cgroup object from idr before its grace + * period starts, so we won't be looking up a cgroup while the + * cgroup is being freed. + */ + idr_remove(&cgrp->root->cgroup_idr, cgrp->id); + cgrp->id = -1; + dput(d); set_bit(CGRP_RELEASABLE, &parent->flags); @@ -4895,6 +4912,10 @@ int __init cgroup_init(void) BUG_ON(cgroup_init_root_id(&cgroup_dummy_root, 0, 1)); + err = idr_alloc(&cgroup_dummy_root.cgroup_idr, cgroup_dummy_top, + 0, 1, GFP_KERNEL); + BUG_ON(err < 0); + mutex_unlock(&cgroup_root_mutex); mutex_unlock(&cgroup_mutex); -- cgit v1.2.3-71-gd317 From b414dc09a31d41d696093a4cce9fb2853a5ecd4e Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 31 Jul 2013 09:51:06 +0800 Subject: cgroup: document how cgroup IDs are assigned As cgroup id has been used in netprio cgroup and will be used in memcg, it's important to make it clear how a cgroup id is allocated. For example, in netprio cgroup, the id is used as index of anarray. Signed-off-by: Li Zefan Reviewed-by: Michal Hocko Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index cca570e188fb..4dfcd0e1b73e 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -161,7 +161,13 @@ struct cgroup_name { struct cgroup { unsigned long flags; /* "unsigned long" so bitops work */ - int id; /* idr allocated in-hierarchy ID */ + /* + * idr allocated in-hierarchy ID. + * + * The ID of the root cgroup is always 0, and a new cgroup + * will be assigned with a smallest available ID. + */ + int id; /* * We link our 'sibling' struct into our parent's 'children'. -- cgit v1.2.3-71-gd317 From e14880f7bb7e0dc0933af304998371dd543ceb40 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 31 Jul 2013 09:51:31 +0800 Subject: cgroup: implement cgroup_from_id() This will be used as a replacement for css_lookup(). There's a difference with cgroup id and css id. cgroup id starts with 0, while css id starts with 1. v4: - also check if cggroup_mutex is held. - make it an inline function. Signed-off-by: Li Zefan Reviewed-by: Michal Hocko Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 4dfcd0e1b73e..bbf4d89b56a8 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -720,6 +720,24 @@ static inline struct cgroup* task_cgroup(struct task_struct *task, return task_subsys_state(task, subsys_id)->cgroup; } +/** + * cgroup_from_id - lookup cgroup by id + * @ss: cgroup subsys to be looked into + * @id: the cgroup id + * + * Returns the cgroup if there's valid one with @id, otherwise returns NULL. + * Should be called under rcu_read_lock(). + */ +static inline struct cgroup *cgroup_from_id(struct cgroup_subsys *ss, int id) +{ +#ifdef CONFIG_PROVE_RCU + rcu_lockdep_assert(rcu_read_lock_held() || + lockdep_is_held(&cgroup_mutex), + "cgroup_from_id() needs proper protection"); +#endif + return idr_find(&ss->root->cgroup_idr, id); +} + struct cgroup *cgroup_next_sibling(struct cgroup *pos); /** -- cgit v1.2.3-71-gd317 From d6b47b122473885abc882e337ac2d321bbcfb378 Mon Sep 17 00:00:00 2001 From: Ben Guthro Date: Tue, 30 Jul 2013 08:24:52 -0400 Subject: ACPI / sleep: Introduce acpi_os_prepare_extended_sleep() for extended sleep path Like acpi_os_prepare_sleep(), register a callback for use in systems like tboot, and xen, which have system specific requirements outside of ACPICA. This mirrors the functionality in acpi_os_prepare_sleep(), called from acpi_hw_sleep() Signed-off-by: Ben Guthro Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/hwesleep.c | 9 +++++++++ drivers/acpi/osl.c | 24 ++++++++++++++++++++++++ include/linux/acpi.h | 7 +++++++ 3 files changed, 40 insertions(+) (limited to 'include') diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c index 5e5f76230f5e..414076818d40 100644 --- a/drivers/acpi/acpica/hwesleep.c +++ b/drivers/acpi/acpica/hwesleep.c @@ -43,6 +43,7 @@ */ #include +#include #include "accommon.h" #define _COMPONENT ACPI_HARDWARE @@ -128,6 +129,14 @@ acpi_status acpi_hw_extended_sleep(u8 sleep_state) ACPI_FLUSH_CPU_CACHE(); + status = acpi_os_prepare_extended_sleep(sleep_state, + acpi_gbl_sleep_type_a, + acpi_gbl_sleep_type_b); + if (ACPI_SKIP(status)) + return_ACPI_STATUS(AE_OK); + if (ACPI_FAILURE(status)) + return_ACPI_STATUS(status); + /* * Set the SLP_TYP and SLP_EN bits. * diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 6ab2c3505520..a934950ff7a0 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -79,6 +79,8 @@ extern char line_buf[80]; static int (*__acpi_os_prepare_sleep)(u8 sleep_state, u32 pm1a_ctrl, u32 pm1b_ctrl); +static int (*__acpi_os_prepare_extended_sleep)(u8 sleep_state, u32 val_a, + u32 val_b); static acpi_osd_handler acpi_irq_handler; static void *acpi_irq_context; @@ -1779,6 +1781,28 @@ void acpi_os_set_prepare_sleep(int (*func)(u8 sleep_state, __acpi_os_prepare_sleep = func; } +acpi_status acpi_os_prepare_extended_sleep(u8 sleep_state, u32 val_a, + u32 val_b) +{ + int rc = 0; + if (__acpi_os_prepare_extended_sleep) + rc = __acpi_os_prepare_extended_sleep(sleep_state, + val_a, val_b); + if (rc < 0) + return AE_ERROR; + else if (rc > 0) + return AE_CTRL_SKIP; + + return AE_OK; +} + +void acpi_os_set_prepare_extended_sleep(int (*func)(u8 sleep_state, + u32 val_a, u32 val_b)) +{ + __acpi_os_prepare_extended_sleep = func; +} + + void alloc_acpi_hp_work(acpi_handle handle, u32 type, void *context, void (*func)(struct work_struct *work)) { diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 353ba256f368..a5db4aeefa36 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -481,6 +481,13 @@ void acpi_os_set_prepare_sleep(int (*func)(u8 sleep_state, acpi_status acpi_os_prepare_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control); + +void acpi_os_set_prepare_extended_sleep(int (*func)(u8 sleep_state, + u32 val_a, u32 val_b)); + +acpi_status acpi_os_prepare_extended_sleep(u8 sleep_state, + u32 val_a, u32 val_b); + #ifdef CONFIG_X86 void arch_reserve_mem_area(acpi_physical_address addr, size_t size); #else -- cgit v1.2.3-71-gd317 From be6b25d15f5e67bc49b90ed55feb1dd947f4df2c Mon Sep 17 00:00:00 2001 From: Ben Guthro Date: Tue, 30 Jul 2013 08:24:54 -0400 Subject: xen / ACPI: notify xen when reduced hardware sleep is available Use the acpi_os_prepare_extended_sleep() callback to notify xen to make use of the reduced hardware sleep functionality The xen hypervisor change underlying this is commit 62d1a69 ("ACPI: support v5 (reduced HW) sleep interface") on the master branch of git://xenbits.xen.org/xen.git. Signed-off-by: Jan Beulich Signed-off-by: Ben Guthro Acked-by: Konrad Wilk Signed-off-by: Rafael J. Wysocki --- drivers/xen/acpi.c | 41 +++++++++++++++++++++++++++------------- include/xen/acpi.h | 8 ++++++-- include/xen/interface/platform.h | 7 ++++--- 3 files changed, 38 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/xen/acpi.c b/drivers/xen/acpi.c index 119d42a2bf57..90307c0b630c 100644 --- a/drivers/xen/acpi.c +++ b/drivers/xen/acpi.c @@ -35,28 +35,43 @@ #include #include -int xen_acpi_notify_hypervisor_state(u8 sleep_state, - u32 pm1a_cnt, u32 pm1b_cnt) +static int xen_acpi_notify_hypervisor_state(u8 sleep_state, + u32 val_a, u32 val_b, + bool extended) { + unsigned int bits = extended ? 8 : 16; + struct xen_platform_op op = { .cmd = XENPF_enter_acpi_sleep, .interface_version = XENPF_INTERFACE_VERSION, - .u = { - .enter_acpi_sleep = { - .pm1a_cnt_val = (u16)pm1a_cnt, - .pm1b_cnt_val = (u16)pm1b_cnt, - .sleep_state = sleep_state, - }, + .u.enter_acpi_sleep = { + .val_a = (u16)val_a, + .val_b = (u16)val_b, + .sleep_state = sleep_state, + .flags = extended ? XENPF_ACPI_SLEEP_EXTENDED : 0, }, }; - if ((pm1a_cnt & 0xffff0000) || (pm1b_cnt & 0xffff0000)) { - WARN(1, "Using more than 16bits of PM1A/B 0x%x/0x%x!" - "Email xen-devel@lists.xensource.com Thank you.\n", \ - pm1a_cnt, pm1b_cnt); + if (WARN((val_a & (~0 << bits)) || (val_b & (~0 << bits)), + "Using more than %u bits of sleep control values %#x/%#x!" + "Email xen-devel@lists.xen.org - Thank you.\n", \ + bits, val_a, val_b)) return -1; - } HYPERVISOR_dom0_op(&op); return 1; } + +int xen_acpi_notify_hypervisor_sleep(u8 sleep_state, + u32 pm1a_cnt, u32 pm1b_cnt) +{ + return xen_acpi_notify_hypervisor_state(sleep_state, pm1a_cnt, + pm1b_cnt, false); +} + +int xen_acpi_notify_hypervisor_extended_sleep(u8 sleep_state, + u32 val_a, u32 val_b) +{ + return xen_acpi_notify_hypervisor_state(sleep_state, val_a, + val_b, true); +} diff --git a/include/xen/acpi.h b/include/xen/acpi.h index 46aa3d1c1654..4ddd7dc4a61e 100644 --- a/include/xen/acpi.h +++ b/include/xen/acpi.h @@ -75,8 +75,10 @@ static inline int xen_acpi_get_pxm(acpi_handle h) return -ENXIO; } -int xen_acpi_notify_hypervisor_state(u8 sleep_state, +int xen_acpi_notify_hypervisor_sleep(u8 sleep_state, u32 pm1a_cnt, u32 pm1b_cnd); +int xen_acpi_notify_hypervisor_extended_sleep(u8 sleep_state, + u32 val_a, u32 val_b); static inline int xen_acpi_suspend_lowlevel(void) { @@ -93,7 +95,9 @@ static inline void xen_acpi_sleep_register(void) { if (xen_initial_domain()) { acpi_os_set_prepare_sleep( - &xen_acpi_notify_hypervisor_state); + &xen_acpi_notify_hypervisor_sleep); + acpi_os_set_prepare_extended_sleep( + &xen_acpi_notify_hypervisor_extended_sleep); acpi_suspend_lowlevel = xen_acpi_suspend_lowlevel; } diff --git a/include/xen/interface/platform.h b/include/xen/interface/platform.h index c57d5f67f702..f1331e3e7271 100644 --- a/include/xen/interface/platform.h +++ b/include/xen/interface/platform.h @@ -152,10 +152,11 @@ DEFINE_GUEST_HANDLE_STRUCT(xenpf_firmware_info_t); #define XENPF_enter_acpi_sleep 51 struct xenpf_enter_acpi_sleep { /* IN variables */ - uint16_t pm1a_cnt_val; /* PM1a control value. */ - uint16_t pm1b_cnt_val; /* PM1b control value. */ + uint16_t val_a; /* PM1a control / sleep type A. */ + uint16_t val_b; /* PM1b control / sleep type B. */ uint32_t sleep_state; /* Which state to enter (Sn). */ - uint32_t flags; /* Must be zero. */ +#define XENPF_ACPI_SLEEP_EXTENDED 0x00000001 + uint32_t flags; /* XENPF_ACPI_SLEEP_*. */ }; DEFINE_GUEST_HANDLE_STRUCT(xenpf_enter_acpi_sleep_t); -- cgit v1.2.3-71-gd317 From 6704af53fc3c5d4113d67d5ff40943d420966cd8 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 28 Jul 2013 22:54:07 +0200 Subject: netfilter: nf_conntrack: remove net_ratelimit() for LOG_INVALID() Logging of invalid packets has to be explicitly enabled. Rate-limiting these messages is inconsistent with other netfilter logging features and makes debugging harder. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 914d8d900798..b411d7b17dec 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -148,17 +148,10 @@ extern int nf_ct_port_nlattr_tuple_size(void); extern const struct nla_policy nf_ct_port_nla_policy[]; #ifdef CONFIG_SYSCTL -#ifdef DEBUG_INVALID_PACKETS #define LOG_INVALID(net, proto) \ ((net)->ct.sysctl_log_invalid == (proto) || \ (net)->ct.sysctl_log_invalid == IPPROTO_RAW) #else -#define LOG_INVALID(net, proto) \ - (((net)->ct.sysctl_log_invalid == (proto) || \ - (net)->ct.sysctl_log_invalid == IPPROTO_RAW) \ - && net_ratelimit()) -#endif -#else static inline int LOG_INVALID(struct net *net, int proto) { return 0; } #endif /* CONFIG_SYSCTL */ -- cgit v1.2.3-71-gd317 From 312a0c16c1fa9dd7cb5af413cf73b2fe2806c962 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 28 Jul 2013 22:54:08 +0200 Subject: netfilter: nf_conntrack: constify sk_buff argument to nf_ct_attach() Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 4 ++-- net/netfilter/core.c | 7 ++++--- net/netfilter/nf_conntrack_core.c | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index de70f7b45b68..f4bbf2cd22d8 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -314,8 +314,8 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family) #endif /*CONFIG_NETFILTER*/ #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) -extern void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *) __rcu; -extern void nf_ct_attach(struct sk_buff *, struct sk_buff *); +extern void (*ip_ct_attach)(struct sk_buff *, const struct sk_buff *) __rcu; +extern void nf_ct_attach(struct sk_buff *, const struct sk_buff *); extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu; struct nf_conn; diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 2217363ab422..593b16ea45e0 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -234,12 +234,13 @@ EXPORT_SYMBOL(skb_make_writable); /* This does not belong here, but locally generated errors need it if connection tracking in use: without this, connection may not be in hash table, and hence manufactured ICMP or RST packets will not be associated with it. */ -void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *) __rcu __read_mostly; +void (*ip_ct_attach)(struct sk_buff *, const struct sk_buff *) + __rcu __read_mostly; EXPORT_SYMBOL(ip_ct_attach); -void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) +void nf_ct_attach(struct sk_buff *new, const struct sk_buff *skb) { - void (*attach)(struct sk_buff *, struct sk_buff *); + void (*attach)(struct sk_buff *, const struct sk_buff *); if (skb->nfct) { rcu_read_lock(); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 0283baedcdfb..d32afaff72f8 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1192,7 +1192,7 @@ EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_tuple_size); #endif /* Used by ipt_REJECT and ip6t_REJECT. */ -static void nf_conntrack_attach(struct sk_buff *nskb, struct sk_buff *skb) +static void nf_conntrack_attach(struct sk_buff *nskb, const struct sk_buff *skb) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; -- cgit v1.2.3-71-gd317 From fd158d79d33d3c8b693e3e2d8c0e3068d529c2dc Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 29 Jul 2013 15:41:52 +0200 Subject: netfilter: tproxy: remove nf_tproxy_core, keep tw sk assigned to skb The module was "permanent", due to the special tproxy skb->destructor. Nowadays we have tcp early demux and its sock_edemux destructor in networking core which can be used instead. Thanks to early demux changes the input path now also handles "skb->sk is tw socket" correctly, so this no longer needs the special handling introduced with commit d503b30bd648b3cb4e5f50b65d27e389960cc6d9 (netfilter: tproxy: do not assign timewait sockets to skb->sk). Thus: - move assign_sock function to where its needed - don't prevent timewait sockets from being assigned to the skb - remove nf_tproxy_core. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- Documentation/networking/tproxy.txt | 5 ++- include/net/netfilter/nf_tproxy_core.h | 4 --- net/netfilter/Kconfig | 22 +++--------- net/netfilter/Makefile | 3 -- net/netfilter/nf_tproxy_core.c | 62 ---------------------------------- net/netfilter/xt_TPROXY.c | 9 +++++ 6 files changed, 16 insertions(+), 89 deletions(-) delete mode 100644 net/netfilter/nf_tproxy_core.c (limited to 'include') diff --git a/Documentation/networking/tproxy.txt b/Documentation/networking/tproxy.txt index 7b5996d9357e..ec11429e1d42 100644 --- a/Documentation/networking/tproxy.txt +++ b/Documentation/networking/tproxy.txt @@ -2,9 +2,8 @@ Transparent proxy support ========================= This feature adds Linux 2.2-like transparent proxy support to current kernels. -To use it, enable NETFILTER_TPROXY, the socket match and the TPROXY target in -your kernel config. You will need policy routing too, so be sure to enable that -as well. +To use it, enable the socket match and the TPROXY target in your kernel config. +You will need policy routing too, so be sure to enable that as well. 1. Making non-local sockets work diff --git a/include/net/netfilter/nf_tproxy_core.h b/include/net/netfilter/nf_tproxy_core.h index 36d9379d4c4b..975ffa4545a9 100644 --- a/include/net/netfilter/nf_tproxy_core.h +++ b/include/net/netfilter/nf_tproxy_core.h @@ -203,8 +203,4 @@ nf_tproxy_get_sock_v6(struct net *net, const u8 protocol, } #endif -/* assign a socket to the skb -- consumes sk */ -void -nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk); - #endif diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 56d22cae5906..c45fc1a60e0d 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -410,20 +410,6 @@ config NF_NAT_TFTP endif # NF_CONNTRACK -# transparent proxy support -config NETFILTER_TPROXY - tristate "Transparent proxying support" - depends on IP_NF_MANGLE - depends on NETFILTER_ADVANCED - help - This option enables transparent proxying support, that is, - support for handling non-locally bound IPv4 TCP and UDP sockets. - For it to work you will have to configure certain iptables rules - and use policy routing. For more information on how to set it up - see Documentation/networking/tproxy.txt. - - To compile it as a module, choose M here. If unsure, say N. - config NETFILTER_XTABLES tristate "Netfilter Xtables support (required for ip_tables)" default m if NETFILTER_ADVANCED=n @@ -720,10 +706,10 @@ config NETFILTER_XT_TARGET_TEE this clone be rerouted to another nexthop. config NETFILTER_XT_TARGET_TPROXY - tristate '"TPROXY" target support' - depends on NETFILTER_TPROXY + tristate '"TPROXY" target transparent proxying support' depends on NETFILTER_XTABLES depends on NETFILTER_ADVANCED + depends on IP_NF_MANGLE select NF_DEFRAG_IPV4 select NF_DEFRAG_IPV6 if IP6_NF_IPTABLES help @@ -731,6 +717,9 @@ config NETFILTER_XT_TARGET_TPROXY REDIRECT. It can only be used in the mangle table and is useful to redirect traffic to a transparent proxy. It does _not_ depend on Netfilter connection tracking and NAT, unlike REDIRECT. + For it to work you will have to configure certain iptables rules + and use policy routing. For more information on how to set it up + see Documentation/networking/tproxy.txt. To compile it as a module, choose M here. If unsure, say N. @@ -1180,7 +1169,6 @@ config NETFILTER_XT_MATCH_SCTP config NETFILTER_XT_MATCH_SOCKET tristate '"socket" match support' - depends on NETFILTER_TPROXY depends on NETFILTER_XTABLES depends on NETFILTER_ADVANCED depends on !NF_CONNTRACK || NF_CONNTRACK diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index a1abf87d43bf..ebfa7dc747cd 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -61,9 +61,6 @@ obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o obj-$(CONFIG_NF_NAT_SIP) += nf_nat_sip.o obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o -# transparent proxy support -obj-$(CONFIG_NETFILTER_TPROXY) += nf_tproxy_core.o - # generic X tables obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o diff --git a/net/netfilter/nf_tproxy_core.c b/net/netfilter/nf_tproxy_core.c deleted file mode 100644 index 474d621cbc2e..000000000000 --- a/net/netfilter/nf_tproxy_core.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Transparent proxy support for Linux/iptables - * - * Copyright (c) 2006-2007 BalaBit IT Ltd. - * Author: Balazs Scheidler, Krisztian Kovacs - * - * 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 - -#include -#include -#include -#include -#include - - -static void -nf_tproxy_destructor(struct sk_buff *skb) -{ - struct sock *sk = skb->sk; - - skb->sk = NULL; - skb->destructor = NULL; - - if (sk) - sock_put(sk); -} - -/* consumes sk */ -void -nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk) -{ - /* assigning tw sockets complicates things; most - * skb->sk->X checks would have to test sk->sk_state first */ - if (sk->sk_state == TCP_TIME_WAIT) { - inet_twsk_put(inet_twsk(sk)); - return; - } - - skb_orphan(skb); - skb->sk = sk; - skb->destructor = nf_tproxy_destructor; -} -EXPORT_SYMBOL_GPL(nf_tproxy_assign_sock); - -static int __init nf_tproxy_init(void) -{ - pr_info("NF_TPROXY: Transparent proxy support initialized, version 4.1.0\n"); - pr_info("NF_TPROXY: Copyright (c) 2006-2007 BalaBit IT Ltd.\n"); - return 0; -} - -module_init(nf_tproxy_init); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Krisztian Kovacs"); -MODULE_DESCRIPTION("Transparent proxy support core routines"); diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index d7f195388f66..17c40deafa4f 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -117,6 +117,15 @@ tproxy_handle_time_wait4(struct sk_buff *skb, __be32 laddr, __be16 lport, return sk; } +/* assign a socket to the skb -- consumes sk */ +static void +nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk) +{ + skb_orphan(skb); + skb->sk = sk; + skb->destructor = sock_edemux; +} + static unsigned int tproxy_tg4(struct sk_buff *skb, __be32 laddr, __be16 lport, u_int32_t mark_mask, u_int32_t mark_value) -- cgit v1.2.3-71-gd317 From 93742cf8af9dd3b053242b273040aa35fcbf93b3 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 29 Jul 2013 15:41:53 +0200 Subject: netfilter: tproxy: remove nf_tproxy_core.h We've removed nf_tproxy_core.ko, so also remove its header. The lookup helpers are split and then moved to tproxy target/socket match. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tproxy_core.h | 206 --------------------------------- net/netfilter/xt_TPROXY.c | 160 ++++++++++++++++++++++++- net/netfilter/xt_socket.c | 66 ++++++++++- 3 files changed, 220 insertions(+), 212 deletions(-) delete mode 100644 include/net/netfilter/nf_tproxy_core.h (limited to 'include') diff --git a/include/net/netfilter/nf_tproxy_core.h b/include/net/netfilter/nf_tproxy_core.h deleted file mode 100644 index 975ffa4545a9..000000000000 --- a/include/net/netfilter/nf_tproxy_core.h +++ /dev/null @@ -1,206 +0,0 @@ -#ifndef _NF_TPROXY_CORE_H -#define _NF_TPROXY_CORE_H - -#include -#include -#include -#include -#include -#include -#include - -#define NFT_LOOKUP_ANY 0 -#define NFT_LOOKUP_LISTENER 1 -#define NFT_LOOKUP_ESTABLISHED 2 - -/* look up and get a reference to a matching socket */ - - -/* This function is used by the 'TPROXY' target and the 'socket' - * match. The following lookups are supported: - * - * Explicit TProxy target rule - * =========================== - * - * This is used when the user wants to intercept a connection matching - * an explicit iptables rule. In this case the sockets are assumed - * matching in preference order: - * - * - match: if there's a fully established connection matching the - * _packet_ tuple, it is returned, assuming the redirection - * already took place and we process a packet belonging to an - * established connection - * - * - match: if there's a listening socket matching the redirection - * (e.g. on-port & on-ip of the connection), it is returned, - * regardless if it was bound to 0.0.0.0 or an explicit - * address. The reasoning is that if there's an explicit rule, it - * does not really matter if the listener is bound to an interface - * or to 0. The user already stated that he wants redirection - * (since he added the rule). - * - * "socket" match based redirection (no specific rule) - * =================================================== - * - * There are connections with dynamic endpoints (e.g. FTP data - * connection) that the user is unable to add explicit rules - * for. These are taken care of by a generic "socket" rule. It is - * assumed that the proxy application is trusted to open such - * connections without explicit iptables rule (except of course the - * generic 'socket' rule). In this case the following sockets are - * matched in preference order: - * - * - match: if there's a fully established connection matching the - * _packet_ tuple - * - * - match: if there's a non-zero bound listener (possibly with a - * non-local address) We don't accept zero-bound listeners, since - * then local services could intercept traffic going through the - * box. - * - * Please note that there's an overlap between what a TPROXY target - * and a socket match will match. Normally if you have both rules the - * "socket" match will be the first one, effectively all packets - * belonging to established connections going through that one. - */ -static inline struct sock * -nf_tproxy_get_sock_v4(struct net *net, const u8 protocol, - const __be32 saddr, const __be32 daddr, - const __be16 sport, const __be16 dport, - const struct net_device *in, int lookup_type) -{ - struct sock *sk; - - /* look up socket */ - switch (protocol) { - case IPPROTO_TCP: - switch (lookup_type) { - case NFT_LOOKUP_ANY: - sk = __inet_lookup(net, &tcp_hashinfo, - saddr, sport, daddr, dport, - in->ifindex); - break; - case NFT_LOOKUP_LISTENER: - sk = inet_lookup_listener(net, &tcp_hashinfo, - saddr, sport, - daddr, dport, - in->ifindex); - - /* NOTE: we return listeners even if bound to - * 0.0.0.0, those are filtered out in - * xt_socket, since xt_TPROXY needs 0 bound - * listeners too */ - - break; - case NFT_LOOKUP_ESTABLISHED: - sk = inet_lookup_established(net, &tcp_hashinfo, - saddr, sport, daddr, dport, - in->ifindex); - break; - default: - WARN_ON(1); - sk = NULL; - break; - } - break; - case IPPROTO_UDP: - sk = udp4_lib_lookup(net, saddr, sport, daddr, dport, - in->ifindex); - if (sk && lookup_type != NFT_LOOKUP_ANY) { - int connected = (sk->sk_state == TCP_ESTABLISHED); - int wildcard = (inet_sk(sk)->inet_rcv_saddr == 0); - - /* NOTE: we return listeners even if bound to - * 0.0.0.0, those are filtered out in - * xt_socket, since xt_TPROXY needs 0 bound - * listeners too */ - if ((lookup_type == NFT_LOOKUP_ESTABLISHED && (!connected || wildcard)) || - (lookup_type == NFT_LOOKUP_LISTENER && connected)) { - sock_put(sk); - sk = NULL; - } - } - break; - default: - WARN_ON(1); - sk = NULL; - } - - pr_debug("tproxy socket lookup: proto %u %08x:%u -> %08x:%u, lookup type: %d, sock %p\n", - protocol, ntohl(saddr), ntohs(sport), ntohl(daddr), ntohs(dport), lookup_type, sk); - - return sk; -} - -#if IS_ENABLED(CONFIG_IPV6) -static inline struct sock * -nf_tproxy_get_sock_v6(struct net *net, const u8 protocol, - const struct in6_addr *saddr, const struct in6_addr *daddr, - const __be16 sport, const __be16 dport, - const struct net_device *in, int lookup_type) -{ - struct sock *sk; - - /* look up socket */ - switch (protocol) { - case IPPROTO_TCP: - switch (lookup_type) { - case NFT_LOOKUP_ANY: - sk = inet6_lookup(net, &tcp_hashinfo, - saddr, sport, daddr, dport, - in->ifindex); - break; - case NFT_LOOKUP_LISTENER: - sk = inet6_lookup_listener(net, &tcp_hashinfo, - saddr, sport, - daddr, ntohs(dport), - in->ifindex); - - /* NOTE: we return listeners even if bound to - * 0.0.0.0, those are filtered out in - * xt_socket, since xt_TPROXY needs 0 bound - * listeners too */ - - break; - case NFT_LOOKUP_ESTABLISHED: - sk = __inet6_lookup_established(net, &tcp_hashinfo, - saddr, sport, daddr, ntohs(dport), - in->ifindex); - break; - default: - WARN_ON(1); - sk = NULL; - break; - } - break; - case IPPROTO_UDP: - sk = udp6_lib_lookup(net, saddr, sport, daddr, dport, - in->ifindex); - if (sk && lookup_type != NFT_LOOKUP_ANY) { - int connected = (sk->sk_state == TCP_ESTABLISHED); - int wildcard = ipv6_addr_any(&inet6_sk(sk)->rcv_saddr); - - /* NOTE: we return listeners even if bound to - * 0.0.0.0, those are filtered out in - * xt_socket, since xt_TPROXY needs 0 bound - * listeners too */ - if ((lookup_type == NFT_LOOKUP_ESTABLISHED && (!connected || wildcard)) || - (lookup_type == NFT_LOOKUP_LISTENER && connected)) { - sock_put(sk); - sk = NULL; - } - } - break; - default: - WARN_ON(1); - sk = NULL; - } - - pr_debug("tproxy socket lookup: proto %u %pI6:%u -> %pI6:%u, lookup type: %d, sock %p\n", - protocol, saddr, ntohs(sport), daddr, ntohs(dport), lookup_type, sk); - - return sk; -} -#endif - -#endif diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index 17c40deafa4f..851383a7f461 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -26,13 +28,18 @@ #define XT_TPROXY_HAVE_IPV6 1 #include #include +#include #include #include #endif -#include #include +enum nf_tproxy_lookup_t { + NFT_LOOKUP_LISTENER, + NFT_LOOKUP_ESTABLISHED, +}; + static bool tproxy_sk_is_transparent(struct sock *sk) { if (sk->sk_state != TCP_TIME_WAIT) { @@ -68,6 +75,157 @@ tproxy_laddr4(struct sk_buff *skb, __be32 user_laddr, __be32 daddr) return laddr ? laddr : daddr; } +/* + * This is used when the user wants to intercept a connection matching + * an explicit iptables rule. In this case the sockets are assumed + * matching in preference order: + * + * - match: if there's a fully established connection matching the + * _packet_ tuple, it is returned, assuming the redirection + * already took place and we process a packet belonging to an + * established connection + * + * - match: if there's a listening socket matching the redirection + * (e.g. on-port & on-ip of the connection), it is returned, + * regardless if it was bound to 0.0.0.0 or an explicit + * address. The reasoning is that if there's an explicit rule, it + * does not really matter if the listener is bound to an interface + * or to 0. The user already stated that he wants redirection + * (since he added the rule). + * + * Please note that there's an overlap between what a TPROXY target + * and a socket match will match. Normally if you have both rules the + * "socket" match will be the first one, effectively all packets + * belonging to established connections going through that one. + */ +static inline struct sock * +nf_tproxy_get_sock_v4(struct net *net, const u8 protocol, + const __be32 saddr, const __be32 daddr, + const __be16 sport, const __be16 dport, + const struct net_device *in, + const enum nf_tproxy_lookup_t lookup_type) +{ + struct sock *sk; + + switch (protocol) { + case IPPROTO_TCP: + switch (lookup_type) { + case NFT_LOOKUP_LISTENER: + sk = inet_lookup_listener(net, &tcp_hashinfo, + saddr, sport, + daddr, dport, + in->ifindex); + + /* NOTE: we return listeners even if bound to + * 0.0.0.0, those are filtered out in + * xt_socket, since xt_TPROXY needs 0 bound + * listeners too + */ + break; + case NFT_LOOKUP_ESTABLISHED: + sk = inet_lookup_established(net, &tcp_hashinfo, + saddr, sport, daddr, dport, + in->ifindex); + break; + default: + BUG(); + } + break; + case IPPROTO_UDP: + sk = udp4_lib_lookup(net, saddr, sport, daddr, dport, + in->ifindex); + if (sk) { + int connected = (sk->sk_state == TCP_ESTABLISHED); + int wildcard = (inet_sk(sk)->inet_rcv_saddr == 0); + + /* NOTE: we return listeners even if bound to + * 0.0.0.0, those are filtered out in + * xt_socket, since xt_TPROXY needs 0 bound + * listeners too + */ + if ((lookup_type == NFT_LOOKUP_ESTABLISHED && (!connected || wildcard)) || + (lookup_type == NFT_LOOKUP_LISTENER && connected)) { + sock_put(sk); + sk = NULL; + } + } + break; + default: + WARN_ON(1); + sk = NULL; + } + + pr_debug("tproxy socket lookup: proto %u %08x:%u -> %08x:%u, lookup type: %d, sock %p\n", + protocol, ntohl(saddr), ntohs(sport), ntohl(daddr), ntohs(dport), lookup_type, sk); + + return sk; +} + +#if IS_ENABLED(CONFIG_IPV6) +static inline struct sock * +nf_tproxy_get_sock_v6(struct net *net, const u8 protocol, + const struct in6_addr *saddr, const struct in6_addr *daddr, + const __be16 sport, const __be16 dport, + const struct net_device *in, + const enum nf_tproxy_lookup_t lookup_type) +{ + struct sock *sk; + + switch (protocol) { + case IPPROTO_TCP: + switch (lookup_type) { + case NFT_LOOKUP_LISTENER: + sk = inet6_lookup_listener(net, &tcp_hashinfo, + saddr, sport, + daddr, ntohs(dport), + in->ifindex); + + /* NOTE: we return listeners even if bound to + * 0.0.0.0, those are filtered out in + * xt_socket, since xt_TPROXY needs 0 bound + * listeners too + */ + break; + case NFT_LOOKUP_ESTABLISHED: + sk = __inet6_lookup_established(net, &tcp_hashinfo, + saddr, sport, daddr, ntohs(dport), + in->ifindex); + break; + default: + BUG(); + } + break; + case IPPROTO_UDP: + sk = udp6_lib_lookup(net, saddr, sport, daddr, dport, + in->ifindex); + if (sk) { + int connected = (sk->sk_state == TCP_ESTABLISHED); + int wildcard = ipv6_addr_any(&inet6_sk(sk)->rcv_saddr); + + /* NOTE: we return listeners even if bound to + * 0.0.0.0, those are filtered out in + * xt_socket, since xt_TPROXY needs 0 bound + * listeners too + */ + if ((lookup_type == NFT_LOOKUP_ESTABLISHED && (!connected || wildcard)) || + (lookup_type == NFT_LOOKUP_LISTENER && connected)) { + sock_put(sk); + sk = NULL; + } + } + break; + default: + WARN_ON(1); + sk = NULL; + } + + pr_debug("tproxy socket lookup: proto %u %pI6:%u -> %pI6:%u, lookup type: %d, sock %p\n", + protocol, saddr, ntohs(sport), daddr, ntohs(dport), lookup_type, sk); + + return sk; +} +#endif + /** * tproxy_handle_time_wait4 - handle IPv4 TCP TIME_WAIT reopen redirections * @skb: The skb being processed. diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index f8b71911037a..a7dd108d4063 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -19,12 +19,12 @@ #include #include #include -#include #include #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) #define XT_SOCKET_HAVE_IPV6 1 #include +#include #include #endif @@ -101,6 +101,43 @@ extract_icmp4_fields(const struct sk_buff *skb, return 0; } +/* "socket" match based redirection (no specific rule) + * =================================================== + * + * There are connections with dynamic endpoints (e.g. FTP data + * connection) that the user is unable to add explicit rules + * for. These are taken care of by a generic "socket" rule. It is + * assumed that the proxy application is trusted to open such + * connections without explicit iptables rule (except of course the + * generic 'socket' rule). In this case the following sockets are + * matched in preference order: + * + * - match: if there's a fully established connection matching the + * _packet_ tuple + * + * - match: if there's a non-zero bound listener (possibly with a + * non-local address) We don't accept zero-bound listeners, since + * then local services could intercept traffic going through the + * box. + */ +static struct sock * +xt_socket_get_sock_v4(struct net *net, const u8 protocol, + const __be32 saddr, const __be32 daddr, + const __be16 sport, const __be16 dport, + const struct net_device *in) +{ + switch (protocol) { + case IPPROTO_TCP: + return __inet_lookup(net, &tcp_hashinfo, + saddr, sport, daddr, dport, + in->ifindex); + case IPPROTO_UDP: + return udp4_lib_lookup(net, saddr, sport, daddr, dport, + in->ifindex); + } + return NULL; +} + static bool socket_match(const struct sk_buff *skb, struct xt_action_param *par, const struct xt_socket_mtinfo1 *info) @@ -156,9 +193,9 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, #endif if (!sk) - sk = nf_tproxy_get_sock_v4(dev_net(skb->dev), protocol, + sk = xt_socket_get_sock_v4(dev_net(skb->dev), protocol, saddr, daddr, sport, dport, - par->in, NFT_LOOKUP_ANY); + par->in); if (sk) { bool wildcard; bool transparent = true; @@ -261,6 +298,25 @@ extract_icmp6_fields(const struct sk_buff *skb, return 0; } +static struct sock * +xt_socket_get_sock_v6(struct net *net, const u8 protocol, + const struct in6_addr *saddr, const struct in6_addr *daddr, + const __be16 sport, const __be16 dport, + const struct net_device *in) +{ + switch (protocol) { + case IPPROTO_TCP: + return inet6_lookup(net, &tcp_hashinfo, + saddr, sport, daddr, dport, + in->ifindex); + case IPPROTO_UDP: + return udp6_lib_lookup(net, saddr, sport, daddr, dport, + in->ifindex); + } + + return NULL; +} + static bool socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) { @@ -298,9 +354,9 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) } if (!sk) - sk = nf_tproxy_get_sock_v6(dev_net(skb->dev), tproto, + sk = xt_socket_get_sock_v6(dev_net(skb->dev), tproto, saddr, daddr, sport, dport, - par->in, NFT_LOOKUP_ANY); + par->in); if (sk) { bool wildcard; bool transparent = true; -- cgit v1.2.3-71-gd317 From 02982c27ba1e1bd9f9d4747214e19ca83aa88d0e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 29 Jul 2013 15:41:54 +0200 Subject: netfilter: nf_conntrack: remove duplicate code in ctnetlink ctnetlink contains copy-paste code from death_by_timeout. In order to avoid changing both places in upcoming event delivery patch, export death_by_timeout functionality and use it in the ctnetlink code. Based on earlier patch from Pablo Neira. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack.h | 3 +-- net/netfilter/nf_conntrack_core.c | 29 ++++++++++++++++------------- net/netfilter/nf_conntrack_netlink.c | 18 +++--------------- 3 files changed, 20 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 644d9c223d24..939aced35a02 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -181,8 +181,7 @@ __nf_conntrack_find(struct net *net, u16 zone, const struct nf_conntrack_tuple *tuple); extern int nf_conntrack_hash_check_insert(struct nf_conn *ct); -extern void nf_ct_delete_from_lists(struct nf_conn *ct); -extern void nf_ct_dying_timeout(struct nf_conn *ct); +bool nf_ct_delete(struct nf_conn *ct, u32 pid, int report); extern void nf_conntrack_flush_report(struct net *net, u32 portid, int report); diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index d32afaff72f8..089e408676fa 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -238,7 +238,7 @@ destroy_conntrack(struct nf_conntrack *nfct) nf_conntrack_free(ct); } -void nf_ct_delete_from_lists(struct nf_conn *ct) +static void nf_ct_delete_from_lists(struct nf_conn *ct) { struct net *net = nf_ct_net(ct); @@ -253,7 +253,6 @@ void nf_ct_delete_from_lists(struct nf_conn *ct) &net->ct.dying); spin_unlock_bh(&nf_conntrack_lock); } -EXPORT_SYMBOL_GPL(nf_ct_delete_from_lists); static void death_by_event(unsigned long ul_conntrack) { @@ -275,7 +274,7 @@ static void death_by_event(unsigned long ul_conntrack) nf_ct_put(ct); } -void nf_ct_dying_timeout(struct nf_conn *ct) +static void nf_ct_dying_timeout(struct nf_conn *ct) { struct net *net = nf_ct_net(ct); struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct); @@ -288,27 +287,33 @@ void nf_ct_dying_timeout(struct nf_conn *ct) (prandom_u32() % net->ct.sysctl_events_retry_timeout); add_timer(&ecache->timeout); } -EXPORT_SYMBOL_GPL(nf_ct_dying_timeout); -static void death_by_timeout(unsigned long ul_conntrack) +bool nf_ct_delete(struct nf_conn *ct, u32 portid, int report) { - struct nf_conn *ct = (void *)ul_conntrack; struct nf_conn_tstamp *tstamp; tstamp = nf_conn_tstamp_find(ct); if (tstamp && tstamp->stop == 0) tstamp->stop = ktime_to_ns(ktime_get_real()); - if (!test_bit(IPS_DYING_BIT, &ct->status) && - unlikely(nf_conntrack_event(IPCT_DESTROY, ct) < 0)) { + if (!nf_ct_is_dying(ct) && + unlikely(nf_conntrack_event_report(IPCT_DESTROY, ct, + portid, report) < 0)) { /* destroy event was not delivered */ nf_ct_delete_from_lists(ct); nf_ct_dying_timeout(ct); - return; + return false; } set_bit(IPS_DYING_BIT, &ct->status); nf_ct_delete_from_lists(ct); nf_ct_put(ct); + return true; +} +EXPORT_SYMBOL_GPL(nf_ct_delete); + +static void death_by_timeout(unsigned long ul_conntrack) +{ + nf_ct_delete((struct nf_conn *)ul_conntrack, 0, 0); } /* @@ -643,10 +648,7 @@ static noinline int early_drop(struct net *net, unsigned int hash) return dropped; if (del_timer(&ct->timeout)) { - death_by_timeout((unsigned long)ct); - /* Check if we indeed killed this entry. Reliable event - delivery may have inserted it into the dying list. */ - if (test_bit(IPS_DYING_BIT, &ct->status)) { + if (nf_ct_delete(ct, 0, 0)) { dropped = 1; NF_CT_STAT_INC_ATOMIC(net, early_drop); } @@ -1253,6 +1255,7 @@ void nf_ct_iterate_cleanup(struct net *net, /* Time to push up daises... */ if (del_timer(&ct->timeout)) death_by_timeout((unsigned long)ct); + /* ... else the timer will get him soon. */ nf_ct_put(ct); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index edc410e778f7..e842c0ded79d 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1038,21 +1038,9 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb, } } - if (del_timer(&ct->timeout)) { - if (nf_conntrack_event_report(IPCT_DESTROY, ct, - NETLINK_CB(skb).portid, - nlmsg_report(nlh)) < 0) { - nf_ct_delete_from_lists(ct); - /* we failed to report the event, try later */ - nf_ct_dying_timeout(ct); - nf_ct_put(ct); - return 0; - } - /* death_by_timeout would report the event again */ - set_bit(IPS_DYING_BIT, &ct->status); - nf_ct_delete_from_lists(ct); - nf_ct_put(ct); - } + if (del_timer(&ct->timeout)) + nf_ct_delete(ct, NETLINK_CB(skb).portid, nlmsg_report(nlh)); + nf_ct_put(ct); return 0; -- cgit v1.2.3-71-gd317 From 2d89c68ac78ae432038ef23371d2fa949d725d43 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sun, 28 Jul 2013 22:54:10 +0200 Subject: netfilter: nf_nat: change sequence number adjustments to 32 bits Using 16 bits is too small, when many adjustments happen the offsets might overflow and break the connection. Signed-off-by: Patrick McHardy Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 2 +- include/net/netfilter/nf_conntrack.h | 2 +- include/net/netfilter/nf_nat.h | 2 +- include/net/netfilter/nf_nat_helper.h | 6 +++--- net/netfilter/nf_conntrack_core.c | 2 +- net/netfilter/nf_conntrack_proto_tcp.c | 4 ++-- net/netfilter/nf_nat_helper.c | 8 ++++---- 7 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index f4bbf2cd22d8..655d5d198d49 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -330,7 +330,7 @@ extern struct nfq_ct_hook __rcu *nfq_ct_hook; struct nfq_ct_nat_hook { void (*seq_adjust)(struct sk_buff *skb, struct nf_conn *ct, - u32 ctinfo, int off); + u32 ctinfo, s32 off); }; extern struct nfq_ct_nat_hook __rcu *nfq_ct_nat_hook; #else diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 939aced35a02..e5eb8b62538c 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -234,7 +234,7 @@ static inline bool nf_ct_kill(struct nf_conn *ct) } /* These are for NAT. Icky. */ -extern s16 (*nf_ct_nat_offset)(const struct nf_conn *ct, +extern s32 (*nf_ct_nat_offset)(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq); diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index ad14a799fd2e..e2441413675c 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -19,7 +19,7 @@ struct nf_nat_seq { u_int32_t correction_pos; /* sequence number offset before and after last modification */ - int16_t offset_before, offset_after; + int32_t offset_before, offset_after; }; #include diff --git a/include/net/netfilter/nf_nat_helper.h b/include/net/netfilter/nf_nat_helper.h index b4d6bfc2af03..194c34794923 100644 --- a/include/net/netfilter/nf_nat_helper.h +++ b/include/net/netfilter/nf_nat_helper.h @@ -41,7 +41,7 @@ extern int nf_nat_mangle_udp_packet(struct sk_buff *skb, extern void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo, - __be32 seq, s16 off); + __be32 seq, s32 off); extern int nf_nat_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, @@ -56,11 +56,11 @@ extern int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb, extern void nf_nat_follow_master(struct nf_conn *ct, struct nf_conntrack_expect *this); -extern s16 nf_nat_get_offset(const struct nf_conn *ct, +extern s32 nf_nat_get_offset(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq); extern void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, - u32 dir, int off); + u32 dir, s32 off); #endif diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 089e408676fa..0934611ff9f3 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1695,7 +1695,7 @@ err_stat: return ret; } -s16 (*nf_ct_nat_offset)(const struct nf_conn *ct, +s32 (*nf_ct_nat_offset)(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq); EXPORT_SYMBOL_GPL(nf_ct_nat_offset); diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 7dcc376eea5f..8f308d896324 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -496,7 +496,7 @@ static void tcp_sack(const struct sk_buff *skb, unsigned int dataoff, } #ifdef CONFIG_NF_NAT_NEEDED -static inline s16 nat_offset(const struct nf_conn *ct, +static inline s32 nat_offset(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq) { @@ -525,7 +525,7 @@ static bool tcp_in_window(const struct nf_conn *ct, struct ip_ct_tcp_state *receiver = &state->seen[!dir]; const struct nf_conntrack_tuple *tuple = &ct->tuplehash[dir].tuple; __u32 seq, ack, sack, end, win, swin; - s16 receiver_offset; + s32 receiver_offset; bool res; /* diff --git a/net/netfilter/nf_nat_helper.c b/net/netfilter/nf_nat_helper.c index a7262ed055c6..ff4a589e3e39 100644 --- a/net/netfilter/nf_nat_helper.c +++ b/net/netfilter/nf_nat_helper.c @@ -68,13 +68,13 @@ adjust_tcp_sequence(u32 seq, } /* Get the offset value, for conntrack */ -s16 nf_nat_get_offset(const struct nf_conn *ct, +s32 nf_nat_get_offset(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq) { struct nf_conn_nat *nat = nfct_nat(ct); struct nf_nat_seq *this_way; - s16 offset; + s32 offset; if (!nat) return 0; @@ -143,7 +143,7 @@ static int enlarge_skb(struct sk_buff *skb, unsigned int extra) } void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo, - __be32 seq, s16 off) + __be32 seq, s32 off) { if (!off) return; @@ -370,7 +370,7 @@ nf_nat_seq_adjust(struct sk_buff *skb, struct tcphdr *tcph; int dir; __be32 newseq, newack; - s16 seqoff, ackoff; + s32 seqoff, ackoff; struct nf_conn_nat *nat = nfct_nat(ct); struct nf_nat_seq *this_way, *other_way; int res; -- cgit v1.2.3-71-gd317 From 8fe120b5a665fc869c23f86e4964b801f6e53486 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 31 Jul 2013 19:00:32 +0200 Subject: ASoC: omap-abe-twl6040: Remove support for pdata (legacy boot) Just recently OMAP4 legacy boot support has been removed. No reason to keep the code used by the legacy boot (pdata based) since neither OMAP4 or OMAP5 can boot in this mode. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- include/linux/platform_data/omap-abe-twl6040.h | 49 --------- sound/soc/omap/omap-abe-twl6040.c | 133 ++++++++----------------- 2 files changed, 41 insertions(+), 141 deletions(-) delete mode 100644 include/linux/platform_data/omap-abe-twl6040.h (limited to 'include') diff --git a/include/linux/platform_data/omap-abe-twl6040.h b/include/linux/platform_data/omap-abe-twl6040.h deleted file mode 100644 index 5d298ac10fc2..000000000000 --- a/include/linux/platform_data/omap-abe-twl6040.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * omap-abe-twl6040.h - ASoC machine driver OMAP4+ devices, header. - * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. - * - * Author: Peter Ujfalusi - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef _OMAP_ABE_TWL6040_H_ -#define _OMAP_ABE_TWL6040_H_ - -/* To select if only one channel is connected in a stereo port */ -#define ABE_TWL6040_LEFT (1 << 0) -#define ABE_TWL6040_RIGHT (1 << 1) - -struct omap_abe_twl6040_data { - char *card_name; - /* Feature flags for connected audio pins */ - u8 has_hs; - u8 has_hf; - bool has_ep; - u8 has_aux; - u8 has_vibra; - bool has_dmic; - bool has_hsmic; - bool has_mainmic; - bool has_submic; - u8 has_afm; - /* Other features */ - bool jack_detection; /* board can detect jack events */ - int mclk_freq; /* MCLK frequency speed for twl6040 */ -}; - -#endif /* _OMAP_ABE_TWL6040_H_ */ diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c index 70cd5c7b2e14..ebb13906b3a0 100644 --- a/sound/soc/omap/omap-abe-twl6040.c +++ b/sound/soc/omap/omap-abe-twl6040.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -166,19 +165,10 @@ static const struct snd_soc_dapm_route audio_map[] = { {"AFMR", NULL, "Line In"}, }; -static inline void twl6040_disconnect_pin(struct snd_soc_dapm_context *dapm, - int connected, char *pin) -{ - if (!connected) - snd_soc_dapm_disable_pin(dapm, pin); -} - static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_card *card = codec->card; - struct snd_soc_dapm_context *dapm = &codec->dapm; - struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev); struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card); int hs_trim; int ret = 0; @@ -203,24 +193,6 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET); } - /* - * NULL pdata means we booted with DT. In this case the routing is - * provided and the card is fully routed, no need to mark pins. - */ - if (!pdata) - return ret; - - /* Disable not connected paths if not used */ - twl6040_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); - twl6040_disconnect_pin(dapm, pdata->has_hf, "Ext Spk"); - twl6040_disconnect_pin(dapm, pdata->has_ep, "Earphone Spk"); - twl6040_disconnect_pin(dapm, pdata->has_aux, "Line Out"); - twl6040_disconnect_pin(dapm, pdata->has_vibra, "Vibrator"); - twl6040_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); - twl6040_disconnect_pin(dapm, pdata->has_mainmic, "Main Handset Mic"); - twl6040_disconnect_pin(dapm, pdata->has_submic, "Sub Handset Mic"); - twl6040_disconnect_pin(dapm, pdata->has_afm, "Line In"); - return ret; } @@ -274,13 +246,18 @@ static struct snd_soc_card omap_abe_card = { static int omap_abe_probe(struct platform_device *pdev) { - struct omap_abe_twl6040_data *pdata = dev_get_platdata(&pdev->dev); struct device_node *node = pdev->dev.of_node; struct snd_soc_card *card = &omap_abe_card; + struct device_node *dai_node; struct abe_twl6040 *priv; int num_links = 0; int ret = 0; + if (!node) { + dev_err(&pdev->dev, "of node is missing.\n"); + return -ENODEV; + } + card->dev = &pdev->dev; priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL); @@ -289,78 +266,50 @@ static int omap_abe_probe(struct platform_device *pdev) priv->dmic_codec_dev = ERR_PTR(-EINVAL); - if (node) { - struct device_node *dai_node; - - if (snd_soc_of_parse_card_name(card, "ti,model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } + if (snd_soc_of_parse_card_name(card, "ti,model")) { + dev_err(&pdev->dev, "Card name is not provided\n"); + return -ENODEV; + } - ret = snd_soc_of_parse_audio_routing(card, - "ti,audio-routing"); - if (ret) { - dev_err(&pdev->dev, - "Error while parsing DAPM routing\n"); - return ret; - } + ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "Error while parsing DAPM routing\n"); + return ret; + } - dai_node = of_parse_phandle(node, "ti,mcpdm", 0); - if (!dai_node) { - dev_err(&pdev->dev, "McPDM node is not provided\n"); - return -EINVAL; - } - abe_twl6040_dai_links[0].cpu_dai_name = NULL; - abe_twl6040_dai_links[0].cpu_of_node = dai_node; + dai_node = of_parse_phandle(node, "ti,mcpdm", 0); + if (!dai_node) { + dev_err(&pdev->dev, "McPDM node is not provided\n"); + return -EINVAL; + } + abe_twl6040_dai_links[0].cpu_dai_name = NULL; + abe_twl6040_dai_links[0].cpu_of_node = dai_node; - dai_node = of_parse_phandle(node, "ti,dmic", 0); - if (dai_node) { - num_links = 2; - abe_twl6040_dai_links[1].cpu_dai_name = NULL; - abe_twl6040_dai_links[1].cpu_of_node = dai_node; + dai_node = of_parse_phandle(node, "ti,dmic", 0); + if (dai_node) { + num_links = 2; + abe_twl6040_dai_links[1].cpu_dai_name = NULL; + abe_twl6040_dai_links[1].cpu_of_node = dai_node; - priv->dmic_codec_dev = platform_device_register_simple( + priv->dmic_codec_dev = platform_device_register_simple( "dmic-codec", -1, NULL, 0); - if (IS_ERR(priv->dmic_codec_dev)) { - dev_err(&pdev->dev, - "Can't instantiate dmic-codec\n"); - return PTR_ERR(priv->dmic_codec_dev); - } - } else { - num_links = 1; - } - - priv->jack_detection = of_property_read_bool(node, - "ti,jack-detection"); - of_property_read_u32(node, "ti,mclk-freq", - &priv->mclk_freq); - if (!priv->mclk_freq) { - dev_err(&pdev->dev, "MCLK frequency not provided\n"); - ret = -EINVAL; - goto err_unregister; + if (IS_ERR(priv->dmic_codec_dev)) { + dev_err(&pdev->dev, "Can't instantiate dmic-codec\n"); + return PTR_ERR(priv->dmic_codec_dev); } - - omap_abe_card.fully_routed = 1; - } else if (pdata) { - if (pdata->card_name) { - card->name = pdata->card_name; - } else { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - - if (pdata->has_dmic) - num_links = 2; - else - num_links = 1; - - priv->jack_detection = pdata->jack_detection; - priv->mclk_freq = pdata->mclk_freq; } else { - dev_err(&pdev->dev, "Missing pdata\n"); - return -ENODEV; + num_links = 1; + } + + priv->jack_detection = of_property_read_bool(node, "ti,jack-detection"); + of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq); + if (!priv->mclk_freq) { + dev_err(&pdev->dev, "MCLK frequency not provided\n"); + ret = -EINVAL; + goto err_unregister; } + card->fully_routed = 1; if (!priv->mclk_freq) { dev_err(&pdev->dev, "MCLK frequency missing\n"); -- cgit v1.2.3-71-gd317 From c0155b2da4cd6583b1b729451249ca346e1c05a2 Mon Sep 17 00:00:00 2001 From: Dmitry Popov Date: Wed, 31 Jul 2013 13:39:45 +0400 Subject: tcp: Remove unused tcpct declarations and comments Remove declaration, 4 defines and confusing comment that are no longer used since 1a2c6181c4 ("tcp: Remove TCPCT"). Signed-off-by: Dmitry Popov Acked-by: Christoph Paasch Signed-off-by: David S. Miller --- include/linux/tcp.h | 1 - include/net/tcp.h | 4 ---- net/ipv4/tcp.c | 4 ---- 3 files changed, 9 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 9640803a17a7..d68633452d9b 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -107,7 +107,6 @@ static inline void tcp_clear_options(struct tcp_options_received *rx_opt) * only four options will fit in a standard TCP header */ #define TCP_NUM_SACKS 4 -struct tcp_cookie_values; struct tcp_request_sock_ops; struct tcp_request_sock { diff --git a/include/net/tcp.h b/include/net/tcp.h index 18fc999dae3c..27b652f379ef 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -192,10 +192,6 @@ extern void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCPOLEN_TIMESTAMP 10 #define TCPOLEN_MD5SIG 18 #define TCPOLEN_EXP_FASTOPEN_BASE 4 -#define TCPOLEN_COOKIE_BASE 2 /* Cookie-less header extension */ -#define TCPOLEN_COOKIE_PAIR 3 /* Cookie pair header extension */ -#define TCPOLEN_COOKIE_MIN (TCPOLEN_COOKIE_BASE+TCP_COOKIE_MIN) -#define TCPOLEN_COOKIE_MAX (TCPOLEN_COOKIE_BASE+TCP_COOKIE_MAX) /* But this is what stacks really send out. */ #define TCPOLEN_TSTAMP_ALIGNED 12 diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index c27e81392398..ab64eea042fa 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -410,10 +410,6 @@ void tcp_init_sock(struct sock *sk) icsk->icsk_sync_mss = tcp_sync_mss; - /* Presumed zeroed, in order of appearance: - * cookie_in_always, cookie_out_never, - * s_data_constant, s_data_in, s_data_out - */ sk->sk_sndbuf = sysctl_tcp_wmem[1]; sk->sk_rcvbuf = sysctl_tcp_rmem[1]; -- cgit v1.2.3-71-gd317 From 8b2ff3204909687be26f20d63dcddc8e3d7a6c14 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 22 Jul 2013 04:22:57 -0300 Subject: [media] media: rc: Add rc_open/close and use count to rc_dev This patch adds user count to rc_dev structure, the reason to add this new member is to allow other code like lirc to open rc device directly. In the existing code, rc device is only opened by input subsystem which works ok if we have any input drivers to match. But in case like lirc where there will be no input driver, rc device will be never opened. Having this user count variable will be usefull to allow rc device to be opened from code other than rc-main. This patch also adds rc_open and rc_close functions for other drivers like lirc to open and close rc devices. This functions safely increment and decrement the user count. Other driver wanting to open rc device should call rc_open and rc_close, rather than directly modifying the rc_dev structure. Signed-off-by: Srinivas Kandagatla Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/rc-main.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- include/media/rc-core.h | 4 ++++ 2 files changed, 46 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 1cf382a0b277..1dedebda1cef 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -699,19 +699,50 @@ void rc_keydown_notimeout(struct rc_dev *dev, int scancode, u8 toggle) } EXPORT_SYMBOL_GPL(rc_keydown_notimeout); +int rc_open(struct rc_dev *rdev) +{ + int rval = 0; + + if (!rdev) + return -EINVAL; + + mutex_lock(&rdev->lock); + if (!rdev->users++) + rval = rdev->open(rdev); + + if (rval) + rdev->users--; + + mutex_unlock(&rdev->lock); + + return rval; +} +EXPORT_SYMBOL_GPL(rc_open); + static int ir_open(struct input_dev *idev) { struct rc_dev *rdev = input_get_drvdata(idev); - return rdev->open(rdev); + return rc_open(rdev); +} + +void rc_close(struct rc_dev *rdev) +{ + if (rdev) { + mutex_lock(&rdev->lock); + + if (!--rdev->users) + rdev->close(rdev); + + mutex_unlock(&rdev->lock); + } } +EXPORT_SYMBOL_GPL(rc_close); static void ir_close(struct input_dev *idev) { struct rc_dev *rdev = input_get_drvdata(idev); - - if (rdev) - rdev->close(rdev); + rc_close(rdev); } /* class for /sys/class/rc */ @@ -1076,7 +1107,14 @@ int rc_register_device(struct rc_dev *dev) memcpy(&dev->input_dev->id, &dev->input_id, sizeof(dev->input_id)); dev->input_dev->phys = dev->input_phys; dev->input_dev->name = dev->input_name; + + /* input_register_device can call ir_open, so unlock mutex here */ + mutex_unlock(&dev->lock); + rc = input_register_device(dev->input_dev); + + mutex_lock(&dev->lock); + if (rc) goto out_table; diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 06a75deff553..2f6f1f78d958 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -101,6 +101,7 @@ struct rc_dev { bool idle; u64 allowed_protos; u64 enabled_protocols; + u32 users; u32 scanmask; void *priv; spinlock_t keylock; @@ -142,6 +143,9 @@ void rc_free_device(struct rc_dev *dev); int rc_register_device(struct rc_dev *dev); void rc_unregister_device(struct rc_dev *dev); +int rc_open(struct rc_dev *rdev); +void rc_close(struct rc_dev *rdev); + void rc_repeat(struct rc_dev *dev); void rc_keydown(struct rc_dev *dev, int scancode, u8 toggle); void rc_keydown_notimeout(struct rc_dev *dev, int scancode, u8 toggle); -- cgit v1.2.3-71-gd317 From ca7a722db1c90dfe0dba165ecef01d6ac8cfee0d Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 22 Jul 2013 04:23:07 -0300 Subject: [media] media: lirc: Allow lirc dev to talk to rc device The use case is simple, if any rc device has allowed protocols = RC_TYPE_LIRC and map_name = RC_MAP_LIRC set, the driver open will be never called. The reason for this is, all of the key maps except lirc have some KEYS in there map, so during rc_register_device process these keys are matched against the input drivers and open is performed, so for the case of RC_MAP_EMPTY, a vt/keyboard is matched and the driver open is performed. In case of lirc, there is no match and result is that there is no open performed, however the lirc-dev will go ahead and create a /dev/lirc0 node. Now when lircd/mode2 opens this device, no data is available because the driver was never opened. Other case pointed by Sean Young, As rc device gets opened via the input interface. If the input device is never opened (e.g. embedded with no console) then the rc open is never called and lirc will not work either. So that's another case. lirc_dev seems to have no link with actual rc device w.r.t open/close. This patch adds rc_dev pointer to lirc_driver structure for cases like this, so that it can do the open/close of the real driver in accordance to lircd/mode2 open/close. Without this patch its impossible to open a rc device which has RC_TYPE_LIRC ad RC_MAP_LIRC set. Signed-off-by: Srinivas Kandagatla Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-lirc-codec.c | 1 + drivers/media/rc/lirc_dev.c | 10 ++++++++++ include/media/lirc_dev.h | 1 + 3 files changed, 12 insertions(+) (limited to 'include') diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index e5be920c0599..ed2c8a1ed8ca 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -384,6 +384,7 @@ static int ir_lirc_register(struct rc_dev *dev) drv->code_length = sizeof(struct ir_raw_event) * 8; drv->fops = &lirc_fops; drv->dev = &dev->dev; + drv->rdev = dev; drv->owner = THIS_MODULE; drv->minor = lirc_register_driver(drv); diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 8dc057b273f2..dc5cbffcd5a2 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -467,6 +468,12 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file) goto error; } + if (ir->d.rdev) { + retval = rc_open(ir->d.rdev); + if (retval) + goto error; + } + cdev = ir->cdev; if (try_module_get(cdev->owner)) { ir->open++; @@ -511,6 +518,9 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file) WARN_ON(mutex_lock_killable(&lirc_dev_lock)); + if (ir->d.rdev) + rc_close(ir->d.rdev); + ir->open--; if (ir->attached) { ir->d.set_use_dec(ir->d.data); diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index 168dd0b1bae2..78f0637ca68d 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -139,6 +139,7 @@ struct lirc_driver { struct lirc_buffer *rbuf; int (*set_use_inc) (void *data); void (*set_use_dec) (void *data); + struct rc_dev *rdev; const struct file_operations *fops; struct device *dev; struct module *owner; -- cgit v1.2.3-71-gd317 From ca4c3fc24e293719fe7410c4e63da9b6bc633b83 Mon Sep 17 00:00:00 2001 From: "fan.du" Date: Tue, 30 Jul 2013 08:33:53 +0800 Subject: net: split rt_genid for ipv4 and ipv6 Current net name space has only one genid for both IPv4 and IPv6, it has below drawbacks: - Add/delete an IPv4 address will invalidate all IPv6 routing table entries. - Insert/remove XFRM policy will also invalidate both IPv4/IPv6 routing table entries even when the policy is only applied for one address family. Thus, this patch attempt to split one genid for two to cater for IPv4 and IPv6 separately in a fine granularity. Signed-off-by: Fan Du Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/net_namespace.h | 37 ++++++++++++++++++++++++++++++++----- include/net/netns/ipv4.h | 1 + include/net/netns/ipv6.h | 1 + net/ipv4/route.c | 16 ++++++++-------- net/ipv6/af_inet6.c | 1 + net/ipv6/route.c | 4 ++-- net/xfrm/xfrm_policy.c | 8 +++++++- security/selinux/include/xfrm.h | 7 ++++++- 8 files changed, 58 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 84e37b1ca9e1..1313456a0994 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -119,7 +119,6 @@ struct net { struct netns_ipvs *ipvs; #endif struct sock *diag_nlsk; - atomic_t rt_genid; atomic_t fnhe_genid; }; @@ -333,14 +332,42 @@ static inline void unregister_net_sysctl_table(struct ctl_table_header *header) } #endif -static inline int rt_genid(struct net *net) +static inline int rt_genid_ipv4(struct net *net) { - return atomic_read(&net->rt_genid); + return atomic_read(&net->ipv4.rt_genid); } -static inline void rt_genid_bump(struct net *net) +static inline void rt_genid_bump_ipv4(struct net *net) { - atomic_inc(&net->rt_genid); + atomic_inc(&net->ipv4.rt_genid); +} + +#if IS_ENABLED(CONFIG_IPV6) +static inline int rt_genid_ipv6(struct net *net) +{ + return atomic_read(&net->ipv6.rt_genid); +} + +static inline void rt_genid_bump_ipv6(struct net *net) +{ + atomic_inc(&net->ipv6.rt_genid); +} +#else +static inline int rt_genid_ipv6(struct net *net) +{ + return 0; +} + +static inline void rt_genid_bump_ipv6(struct net *net) +{ +} +#endif + +/* For callers who don't really care about whether it's IPv4 or IPv6 */ +static inline void rt_genid_bump_all(struct net *net) +{ + rt_genid_bump_ipv4(net); + rt_genid_bump_ipv6(net); } static inline int fnhe_genid(struct net *net) diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 2ba9de89e8ec..bf2ec2202c56 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -77,5 +77,6 @@ struct netns_ipv4 { struct fib_rules_ops *mr_rules_ops; #endif #endif + atomic_t rt_genid; }; #endif diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 005e2c2e39a9..0fb2401197c5 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -72,6 +72,7 @@ struct netns_ipv6 { #endif #endif atomic_t dev_addr_genid; + atomic_t rt_genid; }; #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index a9a54a236832..e805481eff72 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -435,12 +435,12 @@ static inline int ip_rt_proc_init(void) static inline bool rt_is_expired(const struct rtable *rth) { - return rth->rt_genid != rt_genid(dev_net(rth->dst.dev)); + return rth->rt_genid != rt_genid_ipv4(dev_net(rth->dst.dev)); } void rt_cache_flush(struct net *net) { - rt_genid_bump(net); + rt_genid_bump_ipv4(net); } static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst, @@ -1458,7 +1458,7 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, #endif rth->dst.output = ip_rt_bug; - rth->rt_genid = rt_genid(dev_net(dev)); + rth->rt_genid = rt_genid_ipv4(dev_net(dev)); rth->rt_flags = RTCF_MULTICAST; rth->rt_type = RTN_MULTICAST; rth->rt_is_input= 1; @@ -1589,7 +1589,7 @@ static int __mkroute_input(struct sk_buff *skb, goto cleanup; } - rth->rt_genid = rt_genid(dev_net(rth->dst.dev)); + rth->rt_genid = rt_genid_ipv4(dev_net(rth->dst.dev)); rth->rt_flags = flags; rth->rt_type = res->type; rth->rt_is_input = 1; @@ -1760,7 +1760,7 @@ local_input: rth->dst.tclassid = itag; #endif - rth->rt_genid = rt_genid(net); + rth->rt_genid = rt_genid_ipv4(net); rth->rt_flags = flags|RTCF_LOCAL; rth->rt_type = res.type; rth->rt_is_input = 1; @@ -1945,7 +1945,7 @@ add: rth->dst.output = ip_output; - rth->rt_genid = rt_genid(dev_net(dev_out)); + rth->rt_genid = rt_genid_ipv4(dev_net(dev_out)); rth->rt_flags = flags; rth->rt_type = type; rth->rt_is_input = 0; @@ -2227,7 +2227,7 @@ struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_or rt->rt_iif = ort->rt_iif; rt->rt_pmtu = ort->rt_pmtu; - rt->rt_genid = rt_genid(net); + rt->rt_genid = rt_genid_ipv4(net); rt->rt_flags = ort->rt_flags; rt->rt_type = ort->rt_type; rt->rt_gateway = ort->rt_gateway; @@ -2665,7 +2665,7 @@ static __net_initdata struct pernet_operations sysctl_route_ops = { static __net_init int rt_genid_init(struct net *net) { - atomic_set(&net->rt_genid, 0); + atomic_set(&net->ipv4.rt_genid, 0); atomic_set(&net->fnhe_genid, 0); get_random_bytes(&net->ipv4.dev_addr_genid, sizeof(net->ipv4.dev_addr_genid)); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index a5ac969aeefe..0d1a9b153fbb 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -766,6 +766,7 @@ static int __net_init inet6_net_init(struct net *net) net->ipv6.sysctl.bindv6only = 0; net->ipv6.sysctl.icmpv6_time = 1*HZ; + atomic_set(&net->ipv6.rt_genid, 0); err = ipv6_init_mibs(net); if (err) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 74ab1f74abcd..ce9616304521 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -283,7 +283,7 @@ static inline struct rt6_info *ip6_dst_alloc(struct net *net, memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst)); rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers); - rt->rt6i_genid = rt_genid(net); + rt->rt6i_genid = rt_genid_ipv6(net); INIT_LIST_HEAD(&rt->rt6i_siblings); } return rt; @@ -1061,7 +1061,7 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) * DST_OBSOLETE_FORCE_CHK which forces validation calls down * into this function always. */ - if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev))) + if (rt->rt6i_genid != rt_genid_ipv6(dev_net(rt->dst.dev))) return NULL; if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index e52cab3591dd..d8da6b8c6ba8 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -660,7 +660,13 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) xfrm_pol_hold(policy); net->xfrm.policy_count[dir]++; atomic_inc(&flow_cache_genid); - rt_genid_bump(net); + + /* After previous checking, family can either be AF_INET or AF_INET6 */ + if (policy->family == AF_INET) + rt_genid_bump_ipv4(net); + else + rt_genid_bump_ipv6(net); + if (delpol) { xfrm_policy_requeue(delpol, policy); __xfrm_policy_unlink(delpol, dir); diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 65f67cb0aefb..6713f04e30ba 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -50,8 +50,13 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall); static inline void selinux_xfrm_notify_policyload(void) { + struct net *net; + atomic_inc(&flow_cache_genid); - rt_genid_bump(&init_net); + rtnl_lock(); + for_each_net(net) + rt_genid_bump_all(net); + rtnl_unlock(); } #else static inline int selinux_xfrm_enabled(void) -- cgit v1.2.3-71-gd317 From f2f872f9272a79a1048877ea14c15576f46c225e Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 30 Jul 2013 17:55:08 -0700 Subject: netem: Introduce skb_orphan_partial() helper Commit 547669d483e578 ("tcp: xps: fix reordering issues") added unexpected reorders in case netem is used in a MQ setup for high performance test bed. ETH=eth0 tc qd del dev $ETH root 2>/dev/null tc qd add dev $ETH root handle 1: mq for i in `seq 1 32` do tc qd add dev $ETH parent 1:$i netem delay 100ms done As all tcp packets are orphaned by netem, TCP stack believes it can set skb->ooo_okay on all packets. In order to allow producers to send more packets, we want to keep sk_wmem_alloc from reaching sk_sndbuf limit. We can do that by accounting one byte per skb in netem queues, so that TCP stack is not fooled too much. Tested: With above MQ/netem setup, scaling number of concurrent flows gives linear results and no reorders/retransmits lpq83:~# for n in 1 10 20 30 40 50 60 70 80 90 100 do echo -n "n:$n " ; ./super_netperf $n -H 10.7.7.84; done n:1 198.46 n:10 2002.69 n:20 4000.98 n:30 6006.35 n:40 8020.93 n:50 10032.3 n:60 12081.9 n:70 13971.3 n:80 16009.7 n:90 17117.3 n:100 17425.5 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sock.h | 1 + net/core/sock.c | 19 +++++++++++++++++++ net/sched/sch_netem.c | 5 +---- 3 files changed, 21 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index b9f2b095b1ab..53d4714709a1 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1520,6 +1520,7 @@ extern struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force, gfp_t priority); extern void sock_wfree(struct sk_buff *skb); +extern void skb_orphan_partial(struct sk_buff *skb); extern void sock_rfree(struct sk_buff *skb); extern void sock_edemux(struct sk_buff *skb); diff --git a/net/core/sock.c b/net/core/sock.c index 85e8de1bc7fd..a753d97434dc 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1576,6 +1576,25 @@ void sock_wfree(struct sk_buff *skb) } EXPORT_SYMBOL(sock_wfree); +void skb_orphan_partial(struct sk_buff *skb) +{ + /* TCP stack sets skb->ooo_okay based on sk_wmem_alloc, + * so we do not completely orphan skb, but transfert all + * accounted bytes but one, to avoid unexpected reorders. + */ + if (skb->destructor == sock_wfree +#ifdef CONFIG_INET + || skb->destructor == tcp_wfree +#endif + ) { + atomic_sub(skb->truesize - 1, &skb->sk->sk_wmem_alloc); + skb->truesize = 1; + } else { + skb_orphan(skb); + } +} +EXPORT_SYMBOL(skb_orphan_partial); + /* * Read buffer destructor automatically called from kfree_skb. */ diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 82f6016d89ab..a6d788d45216 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -412,12 +412,9 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) /* If a delay is expected, orphan the skb. (orphaning usually takes * place at TX completion time, so _before_ the link transit delay) - * Ideally, this orphaning should be done after the rate limiting - * module, because this breaks TCP Small Queue, and other mechanisms - * based on socket sk_wmem_alloc. */ if (q->latency || q->jitter) - skb_orphan(skb); + skb_orphan_partial(skb); /* * If we need to duplicate packet, then re-insert at top of the -- cgit v1.2.3-71-gd317 From c34a761231b56dea4bd205cb9c29ffe37475d232 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 30 Jul 2013 16:11:15 -0700 Subject: net: skb_orphan() changes It is illegal to set skb->sk without corresponding destructor. Its therefore safe for skb_orphan() to not clear skb->sk if skb->destructor is not set. Also avoid clearing skb->destructor if already NULL. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 5afefa01a13c..a95547adff25 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1805,10 +1805,11 @@ static inline void pskb_trim_unique(struct sk_buff *skb, unsigned int len) */ static inline void skb_orphan(struct sk_buff *skb) { - if (skb->destructor) + if (skb->destructor) { skb->destructor(skb); - skb->destructor = NULL; - skb->sk = NULL; + skb->destructor = NULL; + skb->sk = NULL; + } } /** -- cgit v1.2.3-71-gd317 From e09f3cc0184d6b5c3816f921b7ffb67623e5e834 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:59:28 -0700 Subject: clocksource: arch_timer: Make register accessors less error-prone Using an enum for the register we wish to access allows newer compilers to determine if we've forgotten a case in our switch statement. This allows us to remove the BUILD_BUG() instances in the arm64 port, avoiding problems where optimizations may not happen. To try and force better code generation we're currently marking the accessor functions as inline, but newer compilers can ignore the inline keyword unless it's marked __always_inline. Luckily on arm and arm64 inline is __always_inline, but let's make everything __always_inline to be explicit. Suggested-by: Thomas Gleixner Cc: Thomas Gleixner Cc: Mark Rutland Cc: Marc Zyngier Signed-off-by: Stephen Boyd Signed-off-by: Daniel Lezcano Acked-by: Mark Rutland --- arch/arm/include/asm/arch_timer.h | 14 ++++++-------- arch/arm64/include/asm/arch_timer.h | 23 +++++++++-------------- drivers/clocksource/arm_arch_timer.c | 6 +++--- include/clocksource/arm_arch_timer.h | 6 ++++-- 4 files changed, 22 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h index accefe099182..aeb93f38e9c9 100644 --- a/arch/arm/include/asm/arch_timer.h +++ b/arch/arm/include/asm/arch_timer.h @@ -17,7 +17,8 @@ int arch_timer_arch_init(void); * nicely work out which register we want, and chuck away the rest of * the code. At least it does so with a recent GCC (4.6.3). */ -static inline void arch_timer_reg_write(const int access, const int reg, u32 val) +static __always_inline +void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val) { if (access == ARCH_TIMER_PHYS_ACCESS) { switch (reg) { @@ -28,9 +29,7 @@ static inline void arch_timer_reg_write(const int access, const int reg, u32 val asm volatile("mcr p15, 0, %0, c14, c2, 0" : : "r" (val)); break; } - } - - if (access == ARCH_TIMER_VIRT_ACCESS) { + } else if (access == ARCH_TIMER_VIRT_ACCESS) { switch (reg) { case ARCH_TIMER_REG_CTRL: asm volatile("mcr p15, 0, %0, c14, c3, 1" : : "r" (val)); @@ -44,7 +43,8 @@ static inline void arch_timer_reg_write(const int access, const int reg, u32 val isb(); } -static inline u32 arch_timer_reg_read(const int access, const int reg) +static __always_inline +u32 arch_timer_reg_read(int access, enum arch_timer_reg reg) { u32 val = 0; @@ -57,9 +57,7 @@ static inline u32 arch_timer_reg_read(const int access, const int reg) asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val)); break; } - } - - if (access == ARCH_TIMER_VIRT_ACCESS) { + } else if (access == ARCH_TIMER_VIRT_ACCESS) { switch (reg) { case ARCH_TIMER_REG_CTRL: asm volatile("mrc p15, 0, %0, c14, c3, 1" : "=r" (val)); diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h index d56ed11ba9a3..dbca77168e81 100644 --- a/arch/arm64/include/asm/arch_timer.h +++ b/arch/arm64/include/asm/arch_timer.h @@ -26,7 +26,13 @@ #include -static inline void arch_timer_reg_write(int access, int reg, u32 val) +/* + * These register accessors are marked inline so the compiler can + * nicely work out which register we want, and chuck away the rest of + * the code. + */ +static __always_inline +void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val) { if (access == ARCH_TIMER_PHYS_ACCESS) { switch (reg) { @@ -36,8 +42,6 @@ static inline void arch_timer_reg_write(int access, int reg, u32 val) case ARCH_TIMER_REG_TVAL: asm volatile("msr cntp_tval_el0, %0" : : "r" (val)); break; - default: - BUILD_BUG(); } } else if (access == ARCH_TIMER_VIRT_ACCESS) { switch (reg) { @@ -47,17 +51,14 @@ static inline void arch_timer_reg_write(int access, int reg, u32 val) case ARCH_TIMER_REG_TVAL: asm volatile("msr cntv_tval_el0, %0" : : "r" (val)); break; - default: - BUILD_BUG(); } - } else { - BUILD_BUG(); } isb(); } -static inline u32 arch_timer_reg_read(int access, int reg) +static __always_inline +u32 arch_timer_reg_read(int access, enum arch_timer_reg reg) { u32 val; @@ -69,8 +70,6 @@ static inline u32 arch_timer_reg_read(int access, int reg) case ARCH_TIMER_REG_TVAL: asm volatile("mrs %0, cntp_tval_el0" : "=r" (val)); break; - default: - BUILD_BUG(); } } else if (access == ARCH_TIMER_VIRT_ACCESS) { switch (reg) { @@ -80,11 +79,7 @@ static inline u32 arch_timer_reg_read(int access, int reg) case ARCH_TIMER_REG_TVAL: asm volatile("mrs %0, cntv_tval_el0" : "=r" (val)); break; - default: - BUILD_BUG(); } - } else { - BUILD_BUG(); } return val; diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 053d846ab5b1..aa0703847e5d 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -43,7 +43,7 @@ static bool arch_timer_use_virtual = true; * Architected system timer support. */ -static inline irqreturn_t timer_handler(const int access, +static __always_inline irqreturn_t timer_handler(const int access, struct clock_event_device *evt) { unsigned long ctrl; @@ -72,7 +72,7 @@ static irqreturn_t arch_timer_handler_phys(int irq, void *dev_id) return timer_handler(ARCH_TIMER_PHYS_ACCESS, evt); } -static inline void timer_set_mode(const int access, int mode) +static __always_inline void timer_set_mode(const int access, int mode) { unsigned long ctrl; switch (mode) { @@ -99,7 +99,7 @@ static void arch_timer_set_mode_phys(enum clock_event_mode mode, timer_set_mode(ARCH_TIMER_PHYS_ACCESS, mode); } -static inline void set_next_event(const int access, unsigned long evt) +static __always_inline void set_next_event(const int access, unsigned long evt) { unsigned long ctrl; ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL); diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h index c463ce990c48..f3da817b9b8e 100644 --- a/include/clocksource/arm_arch_timer.h +++ b/include/clocksource/arm_arch_timer.h @@ -23,8 +23,10 @@ #define ARCH_TIMER_CTRL_IT_MASK (1 << 1) #define ARCH_TIMER_CTRL_IT_STAT (1 << 2) -#define ARCH_TIMER_REG_CTRL 0 -#define ARCH_TIMER_REG_TVAL 1 +enum arch_timer_reg { + ARCH_TIMER_REG_CTRL, + ARCH_TIMER_REG_TVAL, +}; #define ARCH_TIMER_PHYS_ACCESS 0 #define ARCH_TIMER_VIRT_ACCESS 1 -- cgit v1.2.3-71-gd317 From 220069945b298d3998c6598b081c466dca259929 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 18 Jul 2013 16:59:32 -0700 Subject: clocksource: arch_timer: Add support for memory mapped timers Add support for the memory mapped timers by filling in the read/write functions and adding some parsing code. Note that we only register one clocksource, preferring the cp15 based clocksource over the mmio one. To keep things simple we register one global clockevent. This covers the case of UP and SMP systems with only mmio hardware and systems where the memory mapped timers are used as the broadcast timer in low power modes. The DT binding allows for per-CPU memory mapped timers in case we want to support that in the future, but the code isn't added here. We also don't do much for hypervisor support, although it should be possible to support it by searching for at least two frames where one frame has the virtual capability and then updating KVM timers to support it. Cc: Mark Rutland Cc: Marc Zyngier Cc: Rob Herring Signed-off-by: Stephen Boyd Signed-off-by: Daniel Lezcano Acked-by: Mark Rutland --- drivers/clocksource/arm_arch_timer.c | 402 ++++++++++++++++++++++++++++++----- include/clocksource/arm_arch_timer.h | 4 +- 2 files changed, 350 insertions(+), 56 deletions(-) (limited to 'include') diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index a9ca28447b49..b3df46d9918b 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -16,13 +16,39 @@ #include #include #include +#include #include +#include #include #include #include +#define CNTTIDR 0x08 +#define CNTTIDR_VIRT(n) (BIT(1) << ((n) * 4)) + +#define CNTVCT_LO 0x08 +#define CNTVCT_HI 0x0c +#define CNTFRQ 0x10 +#define CNTP_TVAL 0x28 +#define CNTP_CTL 0x2c +#define CNTV_TVAL 0x38 +#define CNTV_CTL 0x3c + +#define ARCH_CP15_TIMER BIT(0) +#define ARCH_MEM_TIMER BIT(1) +static unsigned arch_timers_present __initdata; + +static void __iomem *arch_counter_base; + +struct arch_timer { + void __iomem *base; + struct clock_event_device evt; +}; + +#define to_arch_timer(e) container_of(e, struct arch_timer, evt) + static u32 arch_timer_rate; enum ppi_nr { @@ -38,6 +64,7 @@ static int arch_timer_ppi[MAX_TIMER_PPI]; static struct clock_event_device __percpu *arch_timer_evt; static bool arch_timer_use_virtual = true; +static bool arch_timer_mem_use_virtual; /* * Architected system timer support. @@ -47,14 +74,62 @@ static __always_inline void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val, struct clock_event_device *clk) { - arch_timer_reg_write_cp15(access, reg, val); + if (access == ARCH_TIMER_MEM_PHYS_ACCESS) { + struct arch_timer *timer = to_arch_timer(clk); + switch (reg) { + case ARCH_TIMER_REG_CTRL: + writel_relaxed(val, timer->base + CNTP_CTL); + break; + case ARCH_TIMER_REG_TVAL: + writel_relaxed(val, timer->base + CNTP_TVAL); + break; + } + } else if (access == ARCH_TIMER_MEM_VIRT_ACCESS) { + struct arch_timer *timer = to_arch_timer(clk); + switch (reg) { + case ARCH_TIMER_REG_CTRL: + writel_relaxed(val, timer->base + CNTV_CTL); + break; + case ARCH_TIMER_REG_TVAL: + writel_relaxed(val, timer->base + CNTV_TVAL); + break; + } + } else { + arch_timer_reg_write_cp15(access, reg, val); + } } static __always_inline u32 arch_timer_reg_read(int access, enum arch_timer_reg reg, struct clock_event_device *clk) { - return arch_timer_reg_read_cp15(access, reg); + u32 val; + + if (access == ARCH_TIMER_MEM_PHYS_ACCESS) { + struct arch_timer *timer = to_arch_timer(clk); + switch (reg) { + case ARCH_TIMER_REG_CTRL: + val = readl_relaxed(timer->base + CNTP_CTL); + break; + case ARCH_TIMER_REG_TVAL: + val = readl_relaxed(timer->base + CNTP_TVAL); + break; + } + } else if (access == ARCH_TIMER_MEM_VIRT_ACCESS) { + struct arch_timer *timer = to_arch_timer(clk); + switch (reg) { + case ARCH_TIMER_REG_CTRL: + val = readl_relaxed(timer->base + CNTV_CTL); + break; + case ARCH_TIMER_REG_TVAL: + val = readl_relaxed(timer->base + CNTV_TVAL); + break; + } + } else { + val = arch_timer_reg_read_cp15(access, reg); + } + + return val; } static __always_inline irqreturn_t timer_handler(const int access, @@ -86,6 +161,20 @@ static irqreturn_t arch_timer_handler_phys(int irq, void *dev_id) return timer_handler(ARCH_TIMER_PHYS_ACCESS, evt); } +static irqreturn_t arch_timer_handler_phys_mem(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + return timer_handler(ARCH_TIMER_MEM_PHYS_ACCESS, evt); +} + +static irqreturn_t arch_timer_handler_virt_mem(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + return timer_handler(ARCH_TIMER_MEM_VIRT_ACCESS, evt); +} + static __always_inline void timer_set_mode(const int access, int mode, struct clock_event_device *clk) { @@ -114,6 +203,18 @@ static void arch_timer_set_mode_phys(enum clock_event_mode mode, timer_set_mode(ARCH_TIMER_PHYS_ACCESS, mode, clk); } +static void arch_timer_set_mode_virt_mem(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + timer_set_mode(ARCH_TIMER_MEM_VIRT_ACCESS, mode, clk); +} + +static void arch_timer_set_mode_phys_mem(enum clock_event_mode mode, + struct clock_event_device *clk) +{ + timer_set_mode(ARCH_TIMER_MEM_PHYS_ACCESS, mode, clk); +} + static __always_inline void set_next_event(const int access, unsigned long evt, struct clock_event_device *clk) { @@ -139,27 +240,62 @@ static int arch_timer_set_next_event_phys(unsigned long evt, return 0; } -static int __cpuinit arch_timer_setup(struct clock_event_device *clk) +static int arch_timer_set_next_event_virt_mem(unsigned long evt, + struct clock_event_device *clk) { - clk->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP; - clk->name = "arch_sys_timer"; - clk->rating = 450; - if (arch_timer_use_virtual) { - clk->irq = arch_timer_ppi[VIRT_PPI]; - clk->set_mode = arch_timer_set_mode_virt; - clk->set_next_event = arch_timer_set_next_event_virt; + set_next_event(ARCH_TIMER_MEM_VIRT_ACCESS, evt, clk); + return 0; +} + +static int arch_timer_set_next_event_phys_mem(unsigned long evt, + struct clock_event_device *clk) +{ + set_next_event(ARCH_TIMER_MEM_PHYS_ACCESS, evt, clk); + return 0; +} + +static void __cpuinit __arch_timer_setup(unsigned type, + struct clock_event_device *clk) +{ + clk->features = CLOCK_EVT_FEAT_ONESHOT; + + if (type == ARCH_CP15_TIMER) { + clk->features |= CLOCK_EVT_FEAT_C3STOP; + clk->name = "arch_sys_timer"; + clk->rating = 450; + clk->cpumask = cpumask_of(smp_processor_id()); + if (arch_timer_use_virtual) { + clk->irq = arch_timer_ppi[VIRT_PPI]; + clk->set_mode = arch_timer_set_mode_virt; + clk->set_next_event = arch_timer_set_next_event_virt; + } else { + clk->irq = arch_timer_ppi[PHYS_SECURE_PPI]; + clk->set_mode = arch_timer_set_mode_phys; + clk->set_next_event = arch_timer_set_next_event_phys; + } } else { - clk->irq = arch_timer_ppi[PHYS_SECURE_PPI]; - clk->set_mode = arch_timer_set_mode_phys; - clk->set_next_event = arch_timer_set_next_event_phys; + clk->name = "arch_mem_timer"; + clk->rating = 400; + clk->cpumask = cpu_all_mask; + if (arch_timer_mem_use_virtual) { + clk->set_mode = arch_timer_set_mode_virt_mem; + clk->set_next_event = + arch_timer_set_next_event_virt_mem; + } else { + clk->set_mode = arch_timer_set_mode_phys_mem; + clk->set_next_event = + arch_timer_set_next_event_phys_mem; + } } - clk->cpumask = cpumask_of(smp_processor_id()); - clk->set_mode(CLOCK_EVT_MODE_SHUTDOWN, clk); - clockevents_config_and_register(clk, arch_timer_rate, - 0xf, 0x7fffffff); + clockevents_config_and_register(clk, arch_timer_rate, 0xf, 0x7fffffff); +} + +static int __cpuinit arch_timer_setup(struct clock_event_device *clk) +{ + __arch_timer_setup(ARCH_CP15_TIMER, clk); if (arch_timer_use_virtual) enable_percpu_irq(arch_timer_ppi[VIRT_PPI], 0); @@ -174,27 +310,41 @@ static int __cpuinit arch_timer_setup(struct clock_event_device *clk) return 0; } -static int arch_timer_available(void) +static void +arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np) { - u32 freq; - - if (arch_timer_rate == 0) { - freq = arch_timer_get_cntfrq(); - - /* Check the timer frequency. */ - if (freq == 0) { - pr_warn("Architected timer frequency not available\n"); - return -EINVAL; - } + /* Who has more than one independent system counter? */ + if (arch_timer_rate) + return; - arch_timer_rate = freq; + /* Try to determine the frequency from the device tree or CNTFRQ */ + if (of_property_read_u32(np, "clock-frequency", &arch_timer_rate)) { + if (cntbase) + arch_timer_rate = readl_relaxed(cntbase + CNTFRQ); + else + arch_timer_rate = arch_timer_get_cntfrq(); } - pr_info_once("Architected local timer running at %lu.%02luMHz (%s).\n", + /* Check the timer frequency. */ + if (arch_timer_rate == 0) + pr_warn("Architected timer frequency not available\n"); +} + +static void arch_timer_banner(unsigned type) +{ + pr_info("Architected %s%s%s timer(s) running at %lu.%02luMHz (%s%s%s).\n", + type & ARCH_CP15_TIMER ? "cp15" : "", + type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? " and " : "", + type & ARCH_MEM_TIMER ? "mmio" : "", (unsigned long)arch_timer_rate / 1000000, (unsigned long)(arch_timer_rate / 10000) % 100, - arch_timer_use_virtual ? "virt" : "phys"); - return 0; + type & ARCH_CP15_TIMER ? + arch_timer_use_virtual ? "virt" : "phys" : + "", + type == (ARCH_CP15_TIMER | ARCH_MEM_TIMER) ? "/" : "", + type & ARCH_MEM_TIMER ? + arch_timer_mem_use_virtual ? "virt" : "phys" : + ""); } u32 arch_timer_get_rate(void) @@ -202,19 +352,35 @@ u32 arch_timer_get_rate(void) return arch_timer_rate; } -u64 arch_timer_read_counter(void) +static u64 arch_counter_get_cntvct_mem(void) { - return arch_counter_get_cntvct(); + u32 vct_lo, vct_hi, tmp_hi; + + do { + vct_hi = readl_relaxed(arch_counter_base + CNTVCT_HI); + vct_lo = readl_relaxed(arch_counter_base + CNTVCT_LO); + tmp_hi = readl_relaxed(arch_counter_base + CNTVCT_HI); + } while (vct_hi != tmp_hi); + + return ((u64) vct_hi << 32) | vct_lo; } +/* + * Default to cp15 based access because arm64 uses this function for + * sched_clock() before DT is probed and the cp15 method is guaranteed + * to exist on arm64. arm doesn't use this before DT is probed so even + * if we don't have the cp15 accessors we won't have a problem. + */ +u64 (*arch_timer_read_counter)(void) = arch_counter_get_cntvct; + static cycle_t arch_counter_read(struct clocksource *cs) { - return arch_counter_get_cntvct(); + return arch_timer_read_counter(); } static cycle_t arch_counter_read_cc(const struct cyclecounter *cc) { - return arch_counter_get_cntvct(); + return arch_timer_read_counter(); } static struct clocksource clocksource_counter = { @@ -237,6 +403,23 @@ struct timecounter *arch_timer_get_timecounter(void) return &timecounter; } +static void __init arch_counter_register(unsigned type) +{ + u64 start_count; + + /* Register the CP15 based counter if we have one */ + if (type & ARCH_CP15_TIMER) + arch_timer_read_counter = arch_counter_get_cntvct; + else + arch_timer_read_counter = arch_counter_get_cntvct_mem; + + start_count = arch_timer_read_counter(); + clocksource_register_hz(&clocksource_counter, arch_timer_rate); + cyclecounter.mult = clocksource_counter.mult; + cyclecounter.shift = clocksource_counter.shift; + timecounter_init(&timecounter, &cyclecounter, start_count); +} + static void __cpuinit arch_timer_stop(struct clock_event_device *clk) { pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n", @@ -281,22 +464,12 @@ static int __init arch_timer_register(void) int err; int ppi; - err = arch_timer_available(); - if (err) - goto out; - arch_timer_evt = alloc_percpu(struct clock_event_device); if (!arch_timer_evt) { err = -ENOMEM; goto out; } - clocksource_register_hz(&clocksource_counter, arch_timer_rate); - cyclecounter.mult = clocksource_counter.mult; - cyclecounter.shift = clocksource_counter.shift; - timecounter_init(&timecounter, &cyclecounter, - arch_counter_get_cntvct()); - if (arch_timer_use_virtual) { ppi = arch_timer_ppi[VIRT_PPI]; err = request_percpu_irq(ppi, arch_timer_handler_virt, @@ -347,24 +520,77 @@ out: return err; } +static int __init arch_timer_mem_register(void __iomem *base, unsigned int irq) +{ + int ret; + irq_handler_t func; + struct arch_timer *t; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) + return -ENOMEM; + + t->base = base; + t->evt.irq = irq; + __arch_timer_setup(ARCH_MEM_TIMER, &t->evt); + + if (arch_timer_mem_use_virtual) + func = arch_timer_handler_virt_mem; + else + func = arch_timer_handler_phys_mem; + + ret = request_irq(irq, func, IRQF_TIMER, "arch_mem_timer", &t->evt); + if (ret) { + pr_err("arch_timer: Failed to request mem timer irq\n"); + kfree(t); + } + + return ret; +} + +static const struct of_device_id arch_timer_of_match[] __initconst = { + { .compatible = "arm,armv7-timer", }, + { .compatible = "arm,armv8-timer", }, + {}, +}; + +static const struct of_device_id arch_timer_mem_of_match[] __initconst = { + { .compatible = "arm,armv7-timer-mem", }, + {}, +}; + +static void __init arch_timer_common_init(void) +{ + unsigned mask = ARCH_CP15_TIMER | ARCH_MEM_TIMER; + + /* Wait until both nodes are probed if we have two timers */ + if ((arch_timers_present & mask) != mask) { + if (of_find_matching_node(NULL, arch_timer_mem_of_match) && + !(arch_timers_present & ARCH_MEM_TIMER)) + return; + if (of_find_matching_node(NULL, arch_timer_of_match) && + !(arch_timers_present & ARCH_CP15_TIMER)) + return; + } + + arch_timer_banner(arch_timers_present); + arch_counter_register(arch_timers_present); + arch_timer_arch_init(); +} + static void __init arch_timer_init(struct device_node *np) { - u32 freq; int i; - if (arch_timer_get_rate()) { + if (arch_timers_present & ARCH_CP15_TIMER) { pr_warn("arch_timer: multiple nodes in dt, skipping\n"); return; } - /* Try to determine the frequency from the device tree or CNTFRQ */ - if (!of_property_read_u32(np, "clock-frequency", &freq)) - arch_timer_rate = freq; - + arch_timers_present |= ARCH_CP15_TIMER; for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++) arch_timer_ppi[i] = irq_of_parse_and_map(np, i); - - of_node_put(np); + arch_timer_detect_rate(NULL, np); /* * If HYP mode is available, we know that the physical timer @@ -385,7 +611,73 @@ static void __init arch_timer_init(struct device_node *np) } arch_timer_register(); - arch_timer_arch_init(); + arch_timer_common_init(); } CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_init); CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_init); + +static void __init arch_timer_mem_init(struct device_node *np) +{ + struct device_node *frame, *best_frame = NULL; + void __iomem *cntctlbase, *base; + unsigned int irq; + u32 cnttidr; + + arch_timers_present |= ARCH_MEM_TIMER; + cntctlbase = of_iomap(np, 0); + if (!cntctlbase) { + pr_err("arch_timer: Can't find CNTCTLBase\n"); + return; + } + + cnttidr = readl_relaxed(cntctlbase + CNTTIDR); + iounmap(cntctlbase); + + /* + * Try to find a virtual capable frame. Otherwise fall back to a + * physical capable frame. + */ + for_each_available_child_of_node(np, frame) { + int n; + + if (of_property_read_u32(frame, "frame-number", &n)) { + pr_err("arch_timer: Missing frame-number\n"); + of_node_put(best_frame); + of_node_put(frame); + return; + } + + if (cnttidr & CNTTIDR_VIRT(n)) { + of_node_put(best_frame); + best_frame = frame; + arch_timer_mem_use_virtual = true; + break; + } + of_node_put(best_frame); + best_frame = of_node_get(frame); + } + + base = arch_counter_base = of_iomap(best_frame, 0); + if (!base) { + pr_err("arch_timer: Can't map frame's registers\n"); + of_node_put(best_frame); + return; + } + + if (arch_timer_mem_use_virtual) + irq = irq_of_parse_and_map(best_frame, 1); + else + irq = irq_of_parse_and_map(best_frame, 0); + of_node_put(best_frame); + if (!irq) { + pr_err("arch_timer: Frame missing %s irq", + arch_timer_mem_use_virtual ? "virt" : "phys"); + return; + } + + arch_timer_detect_rate(base, np); + arch_timer_mem_register(base, irq); + arch_timer_common_init(); +} +CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem", + arch_timer_mem_init); diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h index f3da817b9b8e..93b7f96f9c59 100644 --- a/include/clocksource/arm_arch_timer.h +++ b/include/clocksource/arm_arch_timer.h @@ -30,11 +30,13 @@ enum arch_timer_reg { #define ARCH_TIMER_PHYS_ACCESS 0 #define ARCH_TIMER_VIRT_ACCESS 1 +#define ARCH_TIMER_MEM_PHYS_ACCESS 2 +#define ARCH_TIMER_MEM_VIRT_ACCESS 3 #ifdef CONFIG_ARM_ARCH_TIMER extern u32 arch_timer_get_rate(void); -extern u64 arch_timer_read_counter(void); +extern u64 (*arch_timer_read_counter)(void); extern struct timecounter *arch_timer_get_timecounter(void); #else -- cgit v1.2.3-71-gd317 From 5c15257f93234aaa9775291a041b49eeb38fd95a Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 30 Jul 2013 22:47:13 -0700 Subject: net: Remove extern from include/net/ scheduling prototypes There are a mix of function prototypes with and without extern in the kernel sources. Standardize on not using extern for function prototypes. Function prototypes don't need to be written with extern. extern is assumed by the compiler. Its use is as unnecessary as using auto to declare automatic/local variables in a block. Reflow modified prototypes to 80 columns. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/act_api.h | 60 +++++++++++++++++++++++------------------------ include/net/pkt_cls.h | 42 ++++++++++++++++----------------- include/net/pkt_sched.h | 50 +++++++++++++++++++-------------------- include/net/sch_generic.h | 53 +++++++++++++++++++++-------------------- 4 files changed, 104 insertions(+), 101 deletions(-) (limited to 'include') diff --git a/include/net/act_api.h b/include/net/act_api.h index b8ffac7b6bab..9e90fdff470d 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -82,36 +82,36 @@ struct tc_action_ops { int (*walk)(struct sk_buff *, struct netlink_callback *, int, struct tc_action *); }; -extern struct tcf_common *tcf_hash_lookup(u32 index, - struct tcf_hashinfo *hinfo); -extern void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo); -extern int tcf_hash_release(struct tcf_common *p, int bind, - struct tcf_hashinfo *hinfo); -extern int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb, - int type, struct tc_action *a); -extern u32 tcf_hash_new_index(u32 *idx_gen, struct tcf_hashinfo *hinfo); -extern int tcf_hash_search(struct tc_action *a, u32 index); -extern struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, - int bind, struct tcf_hashinfo *hinfo); -extern struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est, - struct tc_action *a, int size, - int bind, u32 *idx_gen, - struct tcf_hashinfo *hinfo); -extern void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo); +struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo); +void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo); +int tcf_hash_release(struct tcf_common *p, int bind, + struct tcf_hashinfo *hinfo); +int tcf_generic_walker(struct sk_buff *skb, struct netlink_callback *cb, + int type, struct tc_action *a); +u32 tcf_hash_new_index(u32 *idx_gen, struct tcf_hashinfo *hinfo); +int tcf_hash_search(struct tc_action *a, u32 index); +struct tcf_common *tcf_hash_check(u32 index, struct tc_action *a, + int bind, struct tcf_hashinfo *hinfo); +struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est, + struct tc_action *a, int size, + int bind, u32 *idx_gen, + struct tcf_hashinfo *hinfo); +void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo); -extern int tcf_register_action(struct tc_action_ops *a); -extern int tcf_unregister_action(struct tc_action_ops *a); -extern void tcf_action_destroy(struct tc_action *a, int bind); -extern int tcf_action_exec(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res); -extern struct tc_action *tcf_action_init(struct net *net, struct nlattr *nla, - struct nlattr *est, char *n, int ovr, - int bind); -extern struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, - struct nlattr *est, char *n, int ovr, - int bind); -extern int tcf_action_dump(struct sk_buff *skb, struct tc_action *a, int, int); -extern int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int); -extern int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int); -extern int tcf_action_copy_stats (struct sk_buff *,struct tc_action *, int); +int tcf_register_action(struct tc_action_ops *a); +int tcf_unregister_action(struct tc_action_ops *a); +void tcf_action_destroy(struct tc_action *a, int bind); +int tcf_action_exec(struct sk_buff *skb, const struct tc_action *a, + struct tcf_result *res); +struct tc_action *tcf_action_init(struct net *net, struct nlattr *nla, + struct nlattr *est, char *n, int ovr, + int bind); +struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, + struct nlattr *est, char *n, int ovr, + int bind); +int tcf_action_dump(struct sk_buff *skb, struct tc_action *a, int, int); +int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int); +int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int); +int tcf_action_copy_stats(struct sk_buff *, struct tc_action *, int); #endif /* CONFIG_NET_CLS_ACT */ #endif diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 13174509cdfd..2ebef77a2f9a 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -14,8 +14,8 @@ struct tcf_walker { int (*fn)(struct tcf_proto *, unsigned long node, struct tcf_walker *); }; -extern int register_tcf_proto_ops(struct tcf_proto_ops *ops); -extern int unregister_tcf_proto_ops(struct tcf_proto_ops *ops); +int register_tcf_proto_ops(struct tcf_proto_ops *ops); +int unregister_tcf_proto_ops(struct tcf_proto_ops *ops); static inline unsigned long __cls_set_class(unsigned long *clp, unsigned long cl) @@ -126,17 +126,17 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts, return 0; } -extern int tcf_exts_validate(struct net *net, struct tcf_proto *tp, - struct nlattr **tb, struct nlattr *rate_tlv, - struct tcf_exts *exts, - const struct tcf_ext_map *map); -extern void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts); -extern void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst, - struct tcf_exts *src); -extern int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts, - const struct tcf_ext_map *map); -extern int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts, - const struct tcf_ext_map *map); +int tcf_exts_validate(struct net *net, struct tcf_proto *tp, + struct nlattr **tb, struct nlattr *rate_tlv, + struct tcf_exts *exts, + const struct tcf_ext_map *map); +void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts); +void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst, + struct tcf_exts *src); +int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts, + const struct tcf_ext_map *map); +int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts, + const struct tcf_ext_map *map); /** * struct tcf_pkt_info - packet information @@ -239,14 +239,14 @@ struct tcf_ematch_ops { struct list_head link; }; -extern int tcf_em_register(struct tcf_ematch_ops *); -extern void tcf_em_unregister(struct tcf_ematch_ops *); -extern int tcf_em_tree_validate(struct tcf_proto *, struct nlattr *, - struct tcf_ematch_tree *); -extern void tcf_em_tree_destroy(struct tcf_proto *, struct tcf_ematch_tree *); -extern int tcf_em_tree_dump(struct sk_buff *, struct tcf_ematch_tree *, int); -extern int __tcf_em_tree_match(struct sk_buff *, struct tcf_ematch_tree *, - struct tcf_pkt_info *); +int tcf_em_register(struct tcf_ematch_ops *); +void tcf_em_unregister(struct tcf_ematch_ops *); +int tcf_em_tree_validate(struct tcf_proto *, struct nlattr *, + struct tcf_ematch_tree *); +void tcf_em_tree_destroy(struct tcf_proto *, struct tcf_ematch_tree *); +int tcf_em_tree_dump(struct sk_buff *, struct tcf_ematch_tree *, int); +int __tcf_em_tree_match(struct sk_buff *, struct tcf_ematch_tree *, + struct tcf_pkt_info *); /** * tcf_em_tree_change - replace ematch tree of a running classifier diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 388bf8b6d060..f7c24f8fbdc5 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -64,8 +64,8 @@ struct qdisc_watchdog { struct Qdisc *qdisc; }; -extern void qdisc_watchdog_init(struct qdisc_watchdog *wd, struct Qdisc *qdisc); -extern void qdisc_watchdog_schedule_ns(struct qdisc_watchdog *wd, u64 expires); +void qdisc_watchdog_init(struct qdisc_watchdog *wd, struct Qdisc *qdisc); +void qdisc_watchdog_schedule_ns(struct qdisc_watchdog *wd, u64 expires); static inline void qdisc_watchdog_schedule(struct qdisc_watchdog *wd, psched_time_t expires) @@ -73,31 +73,31 @@ static inline void qdisc_watchdog_schedule(struct qdisc_watchdog *wd, qdisc_watchdog_schedule_ns(wd, PSCHED_TICKS2NS(expires)); } -extern void qdisc_watchdog_cancel(struct qdisc_watchdog *wd); +void qdisc_watchdog_cancel(struct qdisc_watchdog *wd); extern struct Qdisc_ops pfifo_qdisc_ops; extern struct Qdisc_ops bfifo_qdisc_ops; extern struct Qdisc_ops pfifo_head_drop_qdisc_ops; -extern int fifo_set_limit(struct Qdisc *q, unsigned int limit); -extern struct Qdisc *fifo_create_dflt(struct Qdisc *sch, struct Qdisc_ops *ops, - unsigned int limit); - -extern int register_qdisc(struct Qdisc_ops *qops); -extern int unregister_qdisc(struct Qdisc_ops *qops); -extern void qdisc_list_del(struct Qdisc *q); -extern struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle); -extern struct Qdisc *qdisc_lookup_class(struct net_device *dev, u32 handle); -extern struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, - struct nlattr *tab); -extern void qdisc_put_rtab(struct qdisc_rate_table *tab); -extern void qdisc_put_stab(struct qdisc_size_table *tab); -extern void qdisc_warn_nonwc(char *txt, struct Qdisc *qdisc); -extern int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q, - struct net_device *dev, struct netdev_queue *txq, - spinlock_t *root_lock); - -extern void __qdisc_run(struct Qdisc *q); +int fifo_set_limit(struct Qdisc *q, unsigned int limit); +struct Qdisc *fifo_create_dflt(struct Qdisc *sch, struct Qdisc_ops *ops, + unsigned int limit); + +int register_qdisc(struct Qdisc_ops *qops); +int unregister_qdisc(struct Qdisc_ops *qops); +void qdisc_list_del(struct Qdisc *q); +struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle); +struct Qdisc *qdisc_lookup_class(struct net_device *dev, u32 handle); +struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, + struct nlattr *tab); +void qdisc_put_rtab(struct qdisc_rate_table *tab); +void qdisc_put_stab(struct qdisc_size_table *tab); +void qdisc_warn_nonwc(char *txt, struct Qdisc *qdisc); +int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q, + struct net_device *dev, struct netdev_queue *txq, + spinlock_t *root_lock); + +void __qdisc_run(struct Qdisc *q); static inline void qdisc_run(struct Qdisc *q) { @@ -105,10 +105,10 @@ static inline void qdisc_run(struct Qdisc *q) __qdisc_run(q); } -extern int tc_classify_compat(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res); -extern int tc_classify(struct sk_buff *skb, const struct tcf_proto *tp, +int tc_classify_compat(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res); +int tc_classify(struct sk_buff *skb, const struct tcf_proto *tp, + struct tcf_result *res); /* Calculate maximal size of packet seen by hard_start_xmit routine of this device. diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 6eab63363e59..ffc9d883b163 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -350,30 +350,32 @@ qdisc_class_find(const struct Qdisc_class_hash *hash, u32 id) return NULL; } -extern int qdisc_class_hash_init(struct Qdisc_class_hash *); -extern void qdisc_class_hash_insert(struct Qdisc_class_hash *, struct Qdisc_class_common *); -extern void qdisc_class_hash_remove(struct Qdisc_class_hash *, struct Qdisc_class_common *); -extern void qdisc_class_hash_grow(struct Qdisc *, struct Qdisc_class_hash *); -extern void qdisc_class_hash_destroy(struct Qdisc_class_hash *); - -extern void dev_init_scheduler(struct net_device *dev); -extern void dev_shutdown(struct net_device *dev); -extern void dev_activate(struct net_device *dev); -extern void dev_deactivate(struct net_device *dev); -extern void dev_deactivate_many(struct list_head *head); -extern struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue, - struct Qdisc *qdisc); -extern void qdisc_reset(struct Qdisc *qdisc); -extern void qdisc_destroy(struct Qdisc *qdisc); -extern void qdisc_tree_decrease_qlen(struct Qdisc *qdisc, unsigned int n); -extern struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, - struct Qdisc_ops *ops); -extern struct Qdisc *qdisc_create_dflt(struct netdev_queue *dev_queue, - struct Qdisc_ops *ops, u32 parentid); -extern void __qdisc_calculate_pkt_len(struct sk_buff *skb, - const struct qdisc_size_table *stab); -extern void tcf_destroy(struct tcf_proto *tp); -extern void tcf_destroy_chain(struct tcf_proto **fl); +int qdisc_class_hash_init(struct Qdisc_class_hash *); +void qdisc_class_hash_insert(struct Qdisc_class_hash *, + struct Qdisc_class_common *); +void qdisc_class_hash_remove(struct Qdisc_class_hash *, + struct Qdisc_class_common *); +void qdisc_class_hash_grow(struct Qdisc *, struct Qdisc_class_hash *); +void qdisc_class_hash_destroy(struct Qdisc_class_hash *); + +void dev_init_scheduler(struct net_device *dev); +void dev_shutdown(struct net_device *dev); +void dev_activate(struct net_device *dev); +void dev_deactivate(struct net_device *dev); +void dev_deactivate_many(struct list_head *head); +struct Qdisc *dev_graft_qdisc(struct netdev_queue *dev_queue, + struct Qdisc *qdisc); +void qdisc_reset(struct Qdisc *qdisc); +void qdisc_destroy(struct Qdisc *qdisc); +void qdisc_tree_decrease_qlen(struct Qdisc *qdisc, unsigned int n); +struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, + struct Qdisc_ops *ops); +struct Qdisc *qdisc_create_dflt(struct netdev_queue *dev_queue, + struct Qdisc_ops *ops, u32 parentid); +void __qdisc_calculate_pkt_len(struct sk_buff *skb, + const struct qdisc_size_table *stab); +void tcf_destroy(struct tcf_proto *tp); +void tcf_destroy_chain(struct tcf_proto **fl); /* Reset all TX qdiscs greater then index of a device. */ static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i) @@ -692,7 +694,8 @@ static inline u64 psched_l2t_ns(const struct psched_ratecfg *r, return ((u64)(len + r->overhead) * r->mult) >> r->shift; } -extern void psched_ratecfg_precompute(struct psched_ratecfg *r, const struct tc_ratespec *conf); +void psched_ratecfg_precompute(struct psched_ratecfg *r, + const struct tc_ratespec *conf); static inline void psched_ratecfg_getrate(struct tc_ratespec *res, const struct psched_ratecfg *r) -- cgit v1.2.3-71-gd317 From 7764a45a8f1fe74d4f7d301eaca2e558e7e2831a Mon Sep 17 00:00:00 2001 From: Stefan Tomanek Date: Thu, 1 Aug 2013 02:17:15 +0200 Subject: fib_rules: add .suppress operation This change adds a new operation to the fib_rules_ops struct; it allows the suppression of routing decisions if certain criteria are not met by its results. The first implemented constraint is a minimum prefix length added to the structures of routing rules. If a rule is added with a minimum prefix length >0, only routes meeting this threshold will be considered. Any other (more general) routing table entries will be ignored. When configuring a system with multiple network uplinks and default routes, it is often convinient to reference the main routing table multiple times - but omitting the default route. Using this patch and a modified "ip" utility, this can be achieved by using the following command sequence: $ ip route add table secuplink default via 10.42.23.1 $ ip rule add pref 100 table main prefixlength 1 $ ip rule add pref 150 fwmark 0xA table secuplink With this setup, packets marked 0xA will be processed by the additional routing table "secuplink", but only if no suitable route in the main routing table can be found. By using a minimal prefixlength of 1, the default route (/0) of the table "main" is hidden to packets processed by rule 100; packets traveling to destinations with more specific routing entries are processed as usual. Signed-off-by: Stefan Tomanek Signed-off-by: David S. Miller --- include/net/fib_rules.h | 4 ++++ include/uapi/linux/fib_rules.h | 2 +- net/core/fib_rules.c | 8 ++++++++ net/ipv4/fib_rules.c | 14 ++++++++++++++ net/ipv6/fib6_rules.c | 13 +++++++++++++ 5 files changed, 40 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index e361f4882426..2f286dce9259 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -18,6 +18,7 @@ struct fib_rule { u32 pref; u32 flags; u32 table; + u8 table_prefixlen_min; u8 action; u32 target; struct fib_rule __rcu *ctarget; @@ -46,6 +47,8 @@ struct fib_rules_ops { int (*action)(struct fib_rule *, struct flowi *, int, struct fib_lookup_arg *); + bool (*suppress)(struct fib_rule *, + struct fib_lookup_arg *); int (*match)(struct fib_rule *, struct flowi *, int); int (*configure)(struct fib_rule *, @@ -80,6 +83,7 @@ struct fib_rules_ops { [FRA_FWMARK] = { .type = NLA_U32 }, \ [FRA_FWMASK] = { .type = NLA_U32 }, \ [FRA_TABLE] = { .type = NLA_U32 }, \ + [FRA_TABLE_PREFIXLEN_MIN] = { .type = NLA_U8 }, \ [FRA_GOTO] = { .type = NLA_U32 } static inline void fib_rule_get(struct fib_rule *rule) diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h index 51da65b68b85..59cd31b3455e 100644 --- a/include/uapi/linux/fib_rules.h +++ b/include/uapi/linux/fib_rules.h @@ -45,7 +45,7 @@ enum { FRA_FLOW, /* flow/class id */ FRA_UNUSED6, FRA_UNUSED7, - FRA_UNUSED8, + FRA_TABLE_PREFIXLEN_MIN, FRA_TABLE, /* Extended table id */ FRA_FWMASK, /* mask for netfilter mark */ FRA_OIFNAME, diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 21735440c44a..2ef5040c99c8 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -226,6 +226,9 @@ jumped: else err = ops->action(rule, fl, flags, arg); + if (!err && ops->suppress && ops->suppress(rule, arg)) + continue; + if (err != -EAGAIN) { if ((arg->flags & FIB_LOOKUP_NOREF) || likely(atomic_inc_not_zero(&rule->refcnt))) { @@ -337,6 +340,8 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh) rule->action = frh->action; rule->flags = frh->flags; rule->table = frh_get_table(frh, tb); + if (tb[FRA_TABLE_PREFIXLEN_MIN]) + rule->table_prefixlen_min = nla_get_u8(tb[FRA_TABLE_PREFIXLEN_MIN]); if (!tb[FRA_PRIORITY] && ops->default_pref) rule->pref = ops->default_pref(ops); @@ -523,6 +528,7 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + nla_total_size(IFNAMSIZ) /* FRA_OIFNAME */ + nla_total_size(4) /* FRA_PRIORITY */ + nla_total_size(4) /* FRA_TABLE */ + + nla_total_size(1) /* FRA_TABLE_PREFIXLEN_MIN */ + nla_total_size(4) /* FRA_FWMARK */ + nla_total_size(4); /* FRA_FWMASK */ @@ -548,6 +554,8 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, frh->table = rule->table; if (nla_put_u32(skb, FRA_TABLE, rule->table)) goto nla_put_failure; + if (nla_put_u8(skb, FRA_TABLE_PREFIXLEN_MIN, rule->table_prefixlen_min)) + goto nla_put_failure; frh->res1 = 0; frh->res2 = 0; frh->action = rule->action; diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 26aa65d1fce4..9f2906679d1f 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -101,6 +101,19 @@ errout: return err; } +static bool fib4_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg *arg) +{ + /* do not accept result if the route does + * not meet the required prefix length + */ + struct fib_result *result = (struct fib_result *) arg->result; + if (result->prefixlen < rule->table_prefixlen_min) { + if (!(arg->flags & FIB_LOOKUP_NOREF)) + fib_info_put(result->fi); + return true; + } + return false; +} static int fib4_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) { @@ -267,6 +280,7 @@ static const struct fib_rules_ops __net_initconst fib4_rules_ops_template = { .rule_size = sizeof(struct fib4_rule), .addr_size = sizeof(u32), .action = fib4_rule_action, + .suppress = fib4_rule_suppress, .match = fib4_rule_match, .configure = fib4_rule_configure, .delete = fib4_rule_delete, diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index 2e1a432867c0..e64e6a55fc4a 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -111,6 +111,18 @@ out: return rt == NULL ? -EAGAIN : 0; } +static bool fib6_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg *arg) +{ + struct rt6_info *rt = (struct rt6_info *) arg->result; + /* do not accept result if the route does + * not meet the required prefix length + */ + if (rt->rt6i_dst.plen < rule->table_prefixlen_min) { + ip6_rt_put(rt); + return true; + } + return false; +} static int fib6_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) { @@ -244,6 +256,7 @@ static const struct fib_rules_ops __net_initconst fib6_rules_ops_template = { .addr_size = sizeof(struct in6_addr), .action = fib6_rule_action, .match = fib6_rule_match, + .suppress = fib6_rule_suppress, .configure = fib6_rule_configure, .compare = fib6_rule_compare, .fill = fib6_rule_fill, -- cgit v1.2.3-71-gd317 From 86e81f0e624b55fa9f1560c3b64bc80e458c5168 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 25 Jul 2013 18:02:31 +0200 Subject: drm/mm: include required headers in drm_mm.h We need BUG_ON(), spinlock_t and standard kernel data-types so include the right headers. Subject: [drm-intel:drm-intel-nightly 154/166] include/drm/drm_mm.h:67:2: error: unknown type name 'spinlock_t' Message-ID: <51f14693.g5HGdcuw2v3m8FOd%fengguang.wu@intel.com> In case it didn't link to it correctly. Somehow this bug doesn't occur here on my machine, hmm. But I think fixing drm_mm.h is better than changing the include-order in drm_vma_manager.h, so this is what I did. Signed-off-by: David Herrmann Signed-off-by: Dave Airlie --- include/drm/drm_mm.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index b87d05e17d46..98cb50ea6acb 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -36,7 +36,10 @@ /* * Generic range manager structs */ +#include +#include #include +#include #ifdef CONFIG_DEBUG_FS #include #endif -- cgit v1.2.3-71-gd317 From e8e54d3c13b35479294f3dceb6bd6ed7a7dc5bf8 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 31 Jul 2013 17:31:32 -0700 Subject: addrconf.h: Remove extern function prototypes There are a mix of function prototypes with and without extern in the kernel sources. Standardize on not using extern for function prototypes. Function prototypes don't need to be written with extern. extern is assumed by the compiler. Its use is as unnecessary as using auto to declare automatic/local variables in a block. Reflow modified prototypes to 80 columns. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/addrconf.h | 160 ++++++++++++++++++++++--------------------------- 1 file changed, 72 insertions(+), 88 deletions(-) (limited to 'include') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index c7b181cb47a6..43fa31a610b8 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -53,51 +53,36 @@ struct prefix_info { #define IN6_ADDR_HSIZE_SHIFT 4 #define IN6_ADDR_HSIZE (1 << IN6_ADDR_HSIZE_SHIFT) -extern int addrconf_init(void); -extern void addrconf_cleanup(void); +int addrconf_init(void); +void addrconf_cleanup(void); -extern int addrconf_add_ifaddr(struct net *net, - void __user *arg); -extern int addrconf_del_ifaddr(struct net *net, - void __user *arg); -extern int addrconf_set_dstaddr(struct net *net, - void __user *arg); +int addrconf_add_ifaddr(struct net *net, void __user *arg); +int addrconf_del_ifaddr(struct net *net, void __user *arg); +int addrconf_set_dstaddr(struct net *net, void __user *arg); -extern int ipv6_chk_addr(struct net *net, - const struct in6_addr *addr, - const struct net_device *dev, - int strict); +int ipv6_chk_addr(struct net *net, const struct in6_addr *addr, + const struct net_device *dev, int strict); #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) -extern int ipv6_chk_home_addr(struct net *net, - const struct in6_addr *addr); +int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr); #endif -extern int ipv6_chk_prefix(const struct in6_addr *addr, - struct net_device *dev); - -extern struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, - const struct in6_addr *addr, - struct net_device *dev, - int strict); - -extern int ipv6_dev_get_saddr(struct net *net, - const struct net_device *dev, - const struct in6_addr *daddr, - unsigned int srcprefs, - struct in6_addr *saddr); -extern int __ipv6_get_lladdr(struct inet6_dev *idev, - struct in6_addr *addr, - unsigned char banned_flags); -extern int ipv6_get_lladdr(struct net_device *dev, - struct in6_addr *addr, - unsigned char banned_flags); -extern int ipv6_rcv_saddr_equal(const struct sock *sk, - const struct sock *sk2); -extern void addrconf_join_solict(struct net_device *dev, - const struct in6_addr *addr); -extern void addrconf_leave_solict(struct inet6_dev *idev, - const struct in6_addr *addr); +int ipv6_chk_prefix(const struct in6_addr *addr, struct net_device *dev); + +struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, + const struct in6_addr *addr, + struct net_device *dev, int strict); + +int ipv6_dev_get_saddr(struct net *net, const struct net_device *dev, + const struct in6_addr *daddr, unsigned int srcprefs, + struct in6_addr *saddr); +int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, + unsigned char banned_flags); +int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, + unsigned char banned_flags); +int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2); +void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr); +void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr); static inline unsigned long addrconf_timeout_fixup(u32 timeout, unsigned int unit) @@ -124,41 +109,38 @@ static inline int addrconf_finite_timeout(unsigned long timeout) /* * IPv6 Address Label subsystem (addrlabel.c) */ -extern int ipv6_addr_label_init(void); -extern void ipv6_addr_label_cleanup(void); -extern void ipv6_addr_label_rtnl_register(void); -extern u32 ipv6_addr_label(struct net *net, - const struct in6_addr *addr, - int type, int ifindex); +int ipv6_addr_label_init(void); +void ipv6_addr_label_cleanup(void); +void ipv6_addr_label_rtnl_register(void); +u32 ipv6_addr_label(struct net *net, const struct in6_addr *addr, + int type, int ifindex); /* * multicast prototypes (mcast.c) */ -extern int ipv6_sock_mc_join(struct sock *sk, int ifindex, - const struct in6_addr *addr); -extern int ipv6_sock_mc_drop(struct sock *sk, int ifindex, - const struct in6_addr *addr); -extern void ipv6_sock_mc_close(struct sock *sk); -extern bool inet6_mc_check(struct sock *sk, - const struct in6_addr *mc_addr, - const struct in6_addr *src_addr); - -extern int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr); -extern int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr); -extern int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr); -extern void ipv6_mc_up(struct inet6_dev *idev); -extern void ipv6_mc_down(struct inet6_dev *idev); -extern void ipv6_mc_unmap(struct inet6_dev *idev); -extern void ipv6_mc_remap(struct inet6_dev *idev); -extern void ipv6_mc_init_dev(struct inet6_dev *idev); -extern void ipv6_mc_destroy_dev(struct inet6_dev *idev); -extern void addrconf_dad_failure(struct inet6_ifaddr *ifp); - -extern bool ipv6_chk_mcast_addr(struct net_device *dev, - const struct in6_addr *group, - const struct in6_addr *src_addr); - -extern void ipv6_mc_dad_complete(struct inet6_dev *idev); +int ipv6_sock_mc_join(struct sock *sk, int ifindex, + const struct in6_addr *addr); +int ipv6_sock_mc_drop(struct sock *sk, int ifindex, + const struct in6_addr *addr); +void ipv6_sock_mc_close(struct sock *sk); +bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr, + const struct in6_addr *src_addr); + +int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr); +int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr); +int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr); +void ipv6_mc_up(struct inet6_dev *idev); +void ipv6_mc_down(struct inet6_dev *idev); +void ipv6_mc_unmap(struct inet6_dev *idev); +void ipv6_mc_remap(struct inet6_dev *idev); +void ipv6_mc_init_dev(struct inet6_dev *idev); +void ipv6_mc_destroy_dev(struct inet6_dev *idev); +void addrconf_dad_failure(struct inet6_ifaddr *ifp); + +bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group, + const struct in6_addr *src_addr); + +void ipv6_mc_dad_complete(struct inet6_dev *idev); /* * identify MLD packets for MLD filter exceptions */ @@ -184,29 +166,31 @@ static inline bool ipv6_is_mld(struct sk_buff *skb, int nexthdr, int offset) return false; } -extern void addrconf_prefix_rcv(struct net_device *dev, - u8 *opt, int len, bool sllao); +void addrconf_prefix_rcv(struct net_device *dev, + u8 *opt, int len, bool sllao); /* * anycast prototypes (anycast.c) */ -extern int ipv6_sock_ac_join(struct sock *sk,int ifindex, const struct in6_addr *addr); -extern int ipv6_sock_ac_drop(struct sock *sk,int ifindex, const struct in6_addr *addr); -extern void ipv6_sock_ac_close(struct sock *sk); - -extern int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr); -extern int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr); -extern bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev, +int ipv6_sock_ac_join(struct sock *sk, int ifindex, + const struct in6_addr *addr); +int ipv6_sock_ac_drop(struct sock *sk, int ifindex, + const struct in6_addr *addr); +void ipv6_sock_ac_close(struct sock *sk); + +int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr); +int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr); +bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev, const struct in6_addr *addr); /* Device notifier */ -extern int register_inet6addr_notifier(struct notifier_block *nb); -extern int unregister_inet6addr_notifier(struct notifier_block *nb); -extern int inet6addr_notifier_call_chain(unsigned long val, void *v); +int register_inet6addr_notifier(struct notifier_block *nb); +int unregister_inet6addr_notifier(struct notifier_block *nb); +int inet6addr_notifier_call_chain(unsigned long val, void *v); -extern void inet6_netconf_notify_devconf(struct net *net, int type, int ifindex, - struct ipv6_devconf *devconf); +void inet6_netconf_notify_devconf(struct net *net, int type, int ifindex, + struct ipv6_devconf *devconf); /** * __in6_dev_get - get inet6_dev pointer from netdevice @@ -240,7 +224,7 @@ static inline struct inet6_dev *in6_dev_get(const struct net_device *dev) return idev; } -extern void in6_dev_finish_destroy(struct inet6_dev *idev); +void in6_dev_finish_destroy(struct inet6_dev *idev); static inline void in6_dev_put(struct inet6_dev *idev) { @@ -258,7 +242,7 @@ static inline void in6_dev_hold(struct inet6_dev *idev) atomic_inc(&idev->refcnt); } -extern void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp); +void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp); static inline void in6_ifa_put(struct inet6_ifaddr *ifp) { @@ -340,8 +324,8 @@ static inline bool ipv6_addr_is_solict_mult(const struct in6_addr *addr) } #ifdef CONFIG_PROC_FS -extern int if6_proc_init(void); -extern void if6_proc_exit(void); +int if6_proc_init(void); +void if6_proc_exit(void); #endif #endif -- cgit v1.2.3-71-gd317 From b60a8280ba2a34351dd8b98664df3785c27528dd Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 31 Jul 2013 17:31:33 -0700 Subject: af_unix.h: Remove extern from function prototypes There are a mix of function prototypes with and without extern in the kernel sources. Standardize on not using extern for function prototypes. Function prototypes don't need to be written with extern. extern is assumed by the compiler. Its use is as unnecessary as using auto to declare automatic/local variables in a block. Reflow modified prototypes to 80 columns. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/af_unix.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/af_unix.h b/include/net/af_unix.h index dbdfd2b0f3b3..05442df4ca89 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -6,12 +6,12 @@ #include #include -extern void unix_inflight(struct file *fp); -extern void unix_notinflight(struct file *fp); -extern void unix_gc(void); -extern void wait_for_unix_gc(void); -extern struct sock *unix_get_socket(struct file *filp); -extern struct sock *unix_peer_get(struct sock *); +void unix_inflight(struct file *fp); +void unix_notinflight(struct file *fp); +void unix_gc(void); +void wait_for_unix_gc(void); +struct sock *unix_get_socket(struct file *filp); +struct sock *unix_peer_get(struct sock *); #define UNIX_HASH_SIZE 256 #define UNIX_HASH_BITS 8 @@ -71,8 +71,8 @@ long unix_inq_len(struct sock *sk); long unix_outq_len(struct sock *sk); #ifdef CONFIG_SYSCTL -extern int unix_sysctl_register(struct net *net); -extern void unix_sysctl_unregister(struct net *net); +int unix_sysctl_register(struct net *net); +void unix_sysctl_unregister(struct net *net); #else static inline int unix_sysctl_register(struct net *net) { return 0; } static inline void unix_sysctl_unregister(struct net *net) {} -- cgit v1.2.3-71-gd317 From cd2cf63a567fb45be445ccc0aed8e551d86f0314 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 31 Jul 2013 17:31:34 -0700 Subject: af_rxrpc.h: Remove extern from function prototypes There are a mix of function prototypes with and without extern in the kernel sources. Standardize on not using extern for function prototypes. Function prototypes don't need to be written with extern. extern is assumed by the compiler. Its use is as unnecessary as using auto to declare automatic/local variables in a block. Reflow modified prototypes to 80 columns. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/af_rxrpc.h | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h index 03e6e9453623..e797d45a5ae6 100644 --- a/include/net/af_rxrpc.h +++ b/include/net/af_rxrpc.h @@ -31,24 +31,21 @@ enum { typedef void (*rxrpc_interceptor_t)(struct sock *, unsigned long, struct sk_buff *); -extern void rxrpc_kernel_intercept_rx_messages(struct socket *, - rxrpc_interceptor_t); -extern struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *, - struct sockaddr_rxrpc *, - struct key *, - unsigned long, - gfp_t); -extern int rxrpc_kernel_send_data(struct rxrpc_call *, struct msghdr *, - size_t); -extern void rxrpc_kernel_abort_call(struct rxrpc_call *, u32); -extern void rxrpc_kernel_end_call(struct rxrpc_call *); -extern bool rxrpc_kernel_is_data_last(struct sk_buff *); -extern u32 rxrpc_kernel_get_abort_code(struct sk_buff *); -extern int rxrpc_kernel_get_error_number(struct sk_buff *); -extern void rxrpc_kernel_data_delivered(struct sk_buff *); -extern void rxrpc_kernel_free_skb(struct sk_buff *); -extern struct rxrpc_call *rxrpc_kernel_accept_call(struct socket *, - unsigned long); -extern int rxrpc_kernel_reject_call(struct socket *); +void rxrpc_kernel_intercept_rx_messages(struct socket *, rxrpc_interceptor_t); +struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *, + struct sockaddr_rxrpc *, + struct key *, + unsigned long, + gfp_t); +int rxrpc_kernel_send_data(struct rxrpc_call *, struct msghdr *, size_t); +void rxrpc_kernel_abort_call(struct rxrpc_call *, u32); +void rxrpc_kernel_end_call(struct rxrpc_call *); +bool rxrpc_kernel_is_data_last(struct sk_buff *); +u32 rxrpc_kernel_get_abort_code(struct sk_buff *); +int rxrpc_kernel_get_error_number(struct sk_buff *); +void rxrpc_kernel_data_delivered(struct sk_buff *); +void rxrpc_kernel_free_skb(struct sk_buff *); +struct rxrpc_call *rxrpc_kernel_accept_call(struct socket *, unsigned long); +int rxrpc_kernel_reject_call(struct socket *); #endif /* _NET_RXRPC_H */ -- cgit v1.2.3-71-gd317 From 90972b22116bc22181717517ad5b0c7043edb178 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 31 Jul 2013 17:31:35 -0700 Subject: arp/neighbour.h: Remove extern from function prototypes There are a mix of function prototypes with and without extern in the kernel sources. Standardize on not using extern for function prototypes. Function prototypes don't need to be written with extern. extern is assumed by the compiler. Its use is as unnecessary as using auto to declare automatic/local variables in a block. Reflow modified prototypes to 80 columns. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/arp.h | 30 +++++++-------- include/net/neighbour.h | 98 ++++++++++++++++++++++++------------------------- 2 files changed, 63 insertions(+), 65 deletions(-) (limited to 'include') diff --git a/include/net/arp.h b/include/net/arp.h index b630dae03411..7509d9da4e36 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -46,22 +46,22 @@ static inline struct neighbour *__ipv4_neigh_lookup(struct net_device *dev, u32 return n; } -extern void arp_init(void); -extern int arp_find(unsigned char *haddr, struct sk_buff *skb); -extern int arp_ioctl(struct net *net, unsigned int cmd, void __user *arg); -extern void arp_send(int type, int ptype, __be32 dest_ip, - struct net_device *dev, __be32 src_ip, - const unsigned char *dest_hw, - const unsigned char *src_hw, const unsigned char *th); -extern int arp_mc_map(__be32 addr, u8 *haddr, struct net_device *dev, int dir); -extern void arp_ifdown(struct net_device *dev); +void arp_init(void); +int arp_find(unsigned char *haddr, struct sk_buff *skb); +int arp_ioctl(struct net *net, unsigned int cmd, void __user *arg); +void arp_send(int type, int ptype, __be32 dest_ip, + struct net_device *dev, __be32 src_ip, + const unsigned char *dest_hw, + const unsigned char *src_hw, const unsigned char *th); +int arp_mc_map(__be32 addr, u8 *haddr, struct net_device *dev, int dir); +void arp_ifdown(struct net_device *dev); -extern struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip, - struct net_device *dev, __be32 src_ip, - const unsigned char *dest_hw, - const unsigned char *src_hw, - const unsigned char *target_hw); -extern void arp_xmit(struct sk_buff *skb); +struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip, + struct net_device *dev, __be32 src_ip, + const unsigned char *dest_hw, + const unsigned char *src_hw, + const unsigned char *target_hw); +void arp_xmit(struct sk_buff *skb); int arp_invalidate(struct net_device *dev, __be32 ip); #endif /* _ARP_H */ diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 7e748ad8b50c..536501a3e58d 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -195,68 +195,67 @@ static inline void *neighbour_priv(const struct neighbour *n) #define NEIGH_UPDATE_F_ISROUTER 0x40000000 #define NEIGH_UPDATE_F_ADMIN 0x80000000 -extern void neigh_table_init(struct neigh_table *tbl); -extern int neigh_table_clear(struct neigh_table *tbl); -extern struct neighbour * neigh_lookup(struct neigh_table *tbl, - const void *pkey, - struct net_device *dev); -extern struct neighbour * neigh_lookup_nodev(struct neigh_table *tbl, - struct net *net, - const void *pkey); -extern struct neighbour * __neigh_create(struct neigh_table *tbl, - const void *pkey, - struct net_device *dev, - bool want_ref); +void neigh_table_init(struct neigh_table *tbl); +int neigh_table_clear(struct neigh_table *tbl); +struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, + struct net_device *dev); +struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net, + const void *pkey); +struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, + struct net_device *dev, bool want_ref); static inline struct neighbour *neigh_create(struct neigh_table *tbl, const void *pkey, struct net_device *dev) { return __neigh_create(tbl, pkey, dev, true); } -extern void neigh_destroy(struct neighbour *neigh); -extern int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb); -extern int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, - u32 flags); -extern void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev); -extern int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev); -extern int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb); -extern int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb); -extern int neigh_compat_output(struct neighbour *neigh, struct sk_buff *skb); -extern int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb); -extern struct neighbour *neigh_event_ns(struct neigh_table *tbl, +void neigh_destroy(struct neighbour *neigh); +int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb); +int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, u32 flags); +void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev); +int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev); +int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb); +int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb); +int neigh_compat_output(struct neighbour *neigh, struct sk_buff *skb); +int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb); +struct neighbour *neigh_event_ns(struct neigh_table *tbl, u8 *lladdr, void *saddr, struct net_device *dev); -extern struct neigh_parms *neigh_parms_alloc(struct net_device *dev, struct neigh_table *tbl); -extern void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms); +struct neigh_parms *neigh_parms_alloc(struct net_device *dev, + struct neigh_table *tbl); +void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms); static inline -struct net *neigh_parms_net(const struct neigh_parms *parms) +struct net *neigh_parms_net(const struct neigh_parms *parms) { return read_pnet(&parms->net); } -extern unsigned long neigh_rand_reach_time(unsigned long base); +unsigned long neigh_rand_reach_time(unsigned long base); -extern void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, - struct sk_buff *skb); -extern struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl, struct net *net, const void *key, struct net_device *dev, int creat); -extern struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl, - struct net *net, - const void *key, - struct net_device *dev); -extern int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *key, struct net_device *dev); +void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, + struct sk_buff *skb); +struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl, struct net *net, + const void *key, struct net_device *dev, + int creat); +struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl, struct net *net, + const void *key, struct net_device *dev); +int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *key, + struct net_device *dev); -static inline -struct net *pneigh_net(const struct pneigh_entry *pneigh) +static inline struct net *pneigh_net(const struct pneigh_entry *pneigh) { return read_pnet(&pneigh->net); } -extern void neigh_app_ns(struct neighbour *n); -extern void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie); -extern void __neigh_for_each_release(struct neigh_table *tbl, int (*cb)(struct neighbour *)); -extern void pneigh_for_each(struct neigh_table *tbl, void (*cb)(struct pneigh_entry *)); +void neigh_app_ns(struct neighbour *n); +void neigh_for_each(struct neigh_table *tbl, + void (*cb)(struct neighbour *, void *), void *cookie); +void __neigh_for_each_release(struct neigh_table *tbl, + int (*cb)(struct neighbour *)); +void pneigh_for_each(struct neigh_table *tbl, + void (*cb)(struct pneigh_entry *)); struct neigh_seq_state { struct seq_net_private p; @@ -270,15 +269,14 @@ struct neigh_seq_state { #define NEIGH_SEQ_IS_PNEIGH 0x00000002 #define NEIGH_SEQ_SKIP_NOARP 0x00000004 }; -extern void *neigh_seq_start(struct seq_file *, loff_t *, struct neigh_table *, unsigned int); -extern void *neigh_seq_next(struct seq_file *, void *, loff_t *); -extern void neigh_seq_stop(struct seq_file *, void *); - -extern int neigh_sysctl_register(struct net_device *dev, - struct neigh_parms *p, - char *p_name, - proc_handler *proc_handler); -extern void neigh_sysctl_unregister(struct neigh_parms *p); +void *neigh_seq_start(struct seq_file *, loff_t *, struct neigh_table *, + unsigned int); +void *neigh_seq_next(struct seq_file *, void *, loff_t *); +void neigh_seq_stop(struct seq_file *, void *); + +int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, + char *p_name, proc_handler *proc_handler); +void neigh_sysctl_unregister(struct neigh_parms *p); static inline void __neigh_parms_put(struct neigh_parms *parms) { -- cgit v1.2.3-71-gd317 From c1d8f8041cd0357643d5d051d1425a17b75e9d2c Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 31 Jul 2013 17:31:36 -0700 Subject: ax25.h: Remove extern from function prototypes There are a mix of function prototypes with and without extern in the kernel sources. Standardize on not using extern for function prototypes. Function prototypes don't need to be written with extern. extern is assumed by the compiler. Its use is as unnecessary as using auto to declare automatic/local variables in a block. Reflow modified prototypes to 80 columns. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/ax25.h | 215 +++++++++++++++++++++++++++-------------------------- 1 file changed, 109 insertions(+), 106 deletions(-) (limited to 'include') diff --git a/include/net/ax25.h b/include/net/ax25.h index 89ed9ac5701f..bf0396e9a5d3 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -195,7 +195,7 @@ static inline void ax25_hold_route(ax25_route *ax25_rt) atomic_inc(&ax25_rt->refcount); } -extern void __ax25_put_route(ax25_route *ax25_rt); +void __ax25_put_route(ax25_route *ax25_rt); static inline void ax25_put_route(ax25_route *ax25_rt) { @@ -272,30 +272,31 @@ static inline __be16 ax25_type_trans(struct sk_buff *skb, struct net_device *dev /* af_ax25.c */ extern struct hlist_head ax25_list; extern spinlock_t ax25_list_lock; -extern void ax25_cb_add(ax25_cb *); +void ax25_cb_add(ax25_cb *); struct sock *ax25_find_listener(ax25_address *, int, struct net_device *, int); struct sock *ax25_get_socket(ax25_address *, ax25_address *, int); -extern ax25_cb *ax25_find_cb(ax25_address *, ax25_address *, ax25_digi *, struct net_device *); -extern void ax25_send_to_raw(ax25_address *, struct sk_buff *, int); -extern void ax25_destroy_socket(ax25_cb *); -extern ax25_cb * __must_check ax25_create_cb(void); -extern void ax25_fillin_cb(ax25_cb *, ax25_dev *); -extern struct sock *ax25_make_new(struct sock *, struct ax25_dev *); +ax25_cb *ax25_find_cb(ax25_address *, ax25_address *, ax25_digi *, + struct net_device *); +void ax25_send_to_raw(ax25_address *, struct sk_buff *, int); +void ax25_destroy_socket(ax25_cb *); +ax25_cb * __must_check ax25_create_cb(void); +void ax25_fillin_cb(ax25_cb *, ax25_dev *); +struct sock *ax25_make_new(struct sock *, struct ax25_dev *); /* ax25_addr.c */ extern const ax25_address ax25_bcast; extern const ax25_address ax25_defaddr; extern const ax25_address null_ax25_address; -extern char *ax2asc(char *buf, const ax25_address *); -extern void asc2ax(ax25_address *addr, const char *callsign); -extern int ax25cmp(const ax25_address *, const ax25_address *); -extern int ax25digicmp(const ax25_digi *, const ax25_digi *); -extern const unsigned char *ax25_addr_parse(const unsigned char *, int, +char *ax2asc(char *buf, const ax25_address *); +void asc2ax(ax25_address *addr, const char *callsign); +int ax25cmp(const ax25_address *, const ax25_address *); +int ax25digicmp(const ax25_digi *, const ax25_digi *); +const unsigned char *ax25_addr_parse(const unsigned char *, int, ax25_address *, ax25_address *, ax25_digi *, int *, int *); -extern int ax25_addr_build(unsigned char *, const ax25_address *, - const ax25_address *, const ax25_digi *, int, int); -extern int ax25_addr_size(const ax25_digi *); -extern void ax25_digi_invert(const ax25_digi *, ax25_digi *); +int ax25_addr_build(unsigned char *, const ax25_address *, + const ax25_address *, const ax25_digi *, int, int); +int ax25_addr_size(const ax25_digi *); +void ax25_digi_invert(const ax25_digi *, ax25_digi *); /* ax25_dev.c */ extern ax25_dev *ax25_dev_list; @@ -306,33 +307,33 @@ static inline ax25_dev *ax25_dev_ax25dev(struct net_device *dev) return dev->ax25_ptr; } -extern ax25_dev *ax25_addr_ax25dev(ax25_address *); -extern void ax25_dev_device_up(struct net_device *); -extern void ax25_dev_device_down(struct net_device *); -extern int ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *); -extern struct net_device *ax25_fwd_dev(struct net_device *); -extern void ax25_dev_free(void); +ax25_dev *ax25_addr_ax25dev(ax25_address *); +void ax25_dev_device_up(struct net_device *); +void ax25_dev_device_down(struct net_device *); +int ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *); +struct net_device *ax25_fwd_dev(struct net_device *); +void ax25_dev_free(void); /* ax25_ds_in.c */ -extern int ax25_ds_frame_in(ax25_cb *, struct sk_buff *, int); +int ax25_ds_frame_in(ax25_cb *, struct sk_buff *, int); /* ax25_ds_subr.c */ -extern void ax25_ds_nr_error_recovery(ax25_cb *); -extern void ax25_ds_enquiry_response(ax25_cb *); -extern void ax25_ds_establish_data_link(ax25_cb *); -extern void ax25_dev_dama_off(ax25_dev *); -extern void ax25_dama_on(ax25_cb *); -extern void ax25_dama_off(ax25_cb *); +void ax25_ds_nr_error_recovery(ax25_cb *); +void ax25_ds_enquiry_response(ax25_cb *); +void ax25_ds_establish_data_link(ax25_cb *); +void ax25_dev_dama_off(ax25_dev *); +void ax25_dama_on(ax25_cb *); +void ax25_dama_off(ax25_cb *); /* ax25_ds_timer.c */ -extern void ax25_ds_setup_timer(ax25_dev *); -extern void ax25_ds_set_timer(ax25_dev *); -extern void ax25_ds_del_timer(ax25_dev *); -extern void ax25_ds_timer(ax25_cb *); -extern void ax25_ds_t1_timeout(ax25_cb *); -extern void ax25_ds_heartbeat_expiry(ax25_cb *); -extern void ax25_ds_t3timer_expiry(ax25_cb *); -extern void ax25_ds_idletimer_expiry(ax25_cb *); +void ax25_ds_setup_timer(ax25_dev *); +void ax25_ds_set_timer(ax25_dev *); +void ax25_ds_del_timer(ax25_dev *); +void ax25_ds_timer(ax25_cb *); +void ax25_ds_t1_timeout(ax25_cb *); +void ax25_ds_heartbeat_expiry(ax25_cb *); +void ax25_ds_t3timer_expiry(ax25_cb *); +void ax25_ds_idletimer_expiry(ax25_cb *); /* ax25_iface.c */ @@ -342,107 +343,109 @@ struct ax25_protocol { int (*func)(struct sk_buff *, ax25_cb *); }; -extern void ax25_register_pid(struct ax25_protocol *ap); -extern void ax25_protocol_release(unsigned int); +void ax25_register_pid(struct ax25_protocol *ap); +void ax25_protocol_release(unsigned int); struct ax25_linkfail { struct hlist_node lf_node; void (*func)(ax25_cb *, int); }; -extern void ax25_linkfail_register(struct ax25_linkfail *lf); -extern void ax25_linkfail_release(struct ax25_linkfail *lf); -extern int __must_check ax25_listen_register(ax25_address *, - struct net_device *); -extern void ax25_listen_release(ax25_address *, struct net_device *); -extern int (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *); -extern int ax25_listen_mine(ax25_address *, struct net_device *); -extern void ax25_link_failed(ax25_cb *, int); -extern int ax25_protocol_is_registered(unsigned int); +void ax25_linkfail_register(struct ax25_linkfail *lf); +void ax25_linkfail_release(struct ax25_linkfail *lf); +int __must_check ax25_listen_register(ax25_address *, struct net_device *); +void ax25_listen_release(ax25_address *, struct net_device *); +int(*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *); +int ax25_listen_mine(ax25_address *, struct net_device *); +void ax25_link_failed(ax25_cb *, int); +int ax25_protocol_is_registered(unsigned int); /* ax25_in.c */ -extern int ax25_rx_iframe(ax25_cb *, struct sk_buff *); -extern int ax25_kiss_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); +int ax25_rx_iframe(ax25_cb *, struct sk_buff *); +int ax25_kiss_rcv(struct sk_buff *, struct net_device *, struct packet_type *, + struct net_device *); /* ax25_ip.c */ -extern int ax25_hard_header(struct sk_buff *, struct net_device *, - unsigned short, const void *, - const void *, unsigned int); -extern int ax25_rebuild_header(struct sk_buff *); +int ax25_hard_header(struct sk_buff *, struct net_device *, unsigned short, + const void *, const void *, unsigned int); +int ax25_rebuild_header(struct sk_buff *); extern const struct header_ops ax25_header_ops; /* ax25_out.c */ -extern ax25_cb *ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct net_device *); -extern void ax25_output(ax25_cb *, int, struct sk_buff *); -extern void ax25_kick(ax25_cb *); -extern void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int); -extern void ax25_queue_xmit(struct sk_buff *skb, struct net_device *dev); -extern int ax25_check_iframes_acked(ax25_cb *, unsigned short); +ax25_cb *ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, + ax25_digi *, struct net_device *); +void ax25_output(ax25_cb *, int, struct sk_buff *); +void ax25_kick(ax25_cb *); +void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int); +void ax25_queue_xmit(struct sk_buff *skb, struct net_device *dev); +int ax25_check_iframes_acked(ax25_cb *, unsigned short); /* ax25_route.c */ -extern void ax25_rt_device_down(struct net_device *); -extern int ax25_rt_ioctl(unsigned int, void __user *); +void ax25_rt_device_down(struct net_device *); +int ax25_rt_ioctl(unsigned int, void __user *); extern const struct file_operations ax25_route_fops; -extern ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev); -extern int ax25_rt_autobind(ax25_cb *, ax25_address *); -extern struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *); -extern void ax25_rt_free(void); +ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev); +int ax25_rt_autobind(ax25_cb *, ax25_address *); +struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, + ax25_address *, ax25_digi *); +void ax25_rt_free(void); /* ax25_std_in.c */ -extern int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int); +int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int); /* ax25_std_subr.c */ -extern void ax25_std_nr_error_recovery(ax25_cb *); -extern void ax25_std_establish_data_link(ax25_cb *); -extern void ax25_std_transmit_enquiry(ax25_cb *); -extern void ax25_std_enquiry_response(ax25_cb *); -extern void ax25_std_timeout_response(ax25_cb *); +void ax25_std_nr_error_recovery(ax25_cb *); +void ax25_std_establish_data_link(ax25_cb *); +void ax25_std_transmit_enquiry(ax25_cb *); +void ax25_std_enquiry_response(ax25_cb *); +void ax25_std_timeout_response(ax25_cb *); /* ax25_std_timer.c */ -extern void ax25_std_heartbeat_expiry(ax25_cb *); -extern void ax25_std_t1timer_expiry(ax25_cb *); -extern void ax25_std_t2timer_expiry(ax25_cb *); -extern void ax25_std_t3timer_expiry(ax25_cb *); -extern void ax25_std_idletimer_expiry(ax25_cb *); +void ax25_std_heartbeat_expiry(ax25_cb *); +void ax25_std_t1timer_expiry(ax25_cb *); +void ax25_std_t2timer_expiry(ax25_cb *); +void ax25_std_t3timer_expiry(ax25_cb *); +void ax25_std_idletimer_expiry(ax25_cb *); /* ax25_subr.c */ -extern void ax25_clear_queues(ax25_cb *); -extern void ax25_frames_acked(ax25_cb *, unsigned short); -extern void ax25_requeue_frames(ax25_cb *); -extern int ax25_validate_nr(ax25_cb *, unsigned short); -extern int ax25_decode(ax25_cb *, struct sk_buff *, int *, int *, int *); -extern void ax25_send_control(ax25_cb *, int, int, int); -extern void ax25_return_dm(struct net_device *, ax25_address *, ax25_address *, ax25_digi *); -extern void ax25_calculate_t1(ax25_cb *); -extern void ax25_calculate_rtt(ax25_cb *); -extern void ax25_disconnect(ax25_cb *, int); +void ax25_clear_queues(ax25_cb *); +void ax25_frames_acked(ax25_cb *, unsigned short); +void ax25_requeue_frames(ax25_cb *); +int ax25_validate_nr(ax25_cb *, unsigned short); +int ax25_decode(ax25_cb *, struct sk_buff *, int *, int *, int *); +void ax25_send_control(ax25_cb *, int, int, int); +void ax25_return_dm(struct net_device *, ax25_address *, ax25_address *, + ax25_digi *); +void ax25_calculate_t1(ax25_cb *); +void ax25_calculate_rtt(ax25_cb *); +void ax25_disconnect(ax25_cb *, int); /* ax25_timer.c */ -extern void ax25_setup_timers(ax25_cb *); -extern void ax25_start_heartbeat(ax25_cb *); -extern void ax25_start_t1timer(ax25_cb *); -extern void ax25_start_t2timer(ax25_cb *); -extern void ax25_start_t3timer(ax25_cb *); -extern void ax25_start_idletimer(ax25_cb *); -extern void ax25_stop_heartbeat(ax25_cb *); -extern void ax25_stop_t1timer(ax25_cb *); -extern void ax25_stop_t2timer(ax25_cb *); -extern void ax25_stop_t3timer(ax25_cb *); -extern void ax25_stop_idletimer(ax25_cb *); -extern int ax25_t1timer_running(ax25_cb *); -extern unsigned long ax25_display_timer(struct timer_list *); +void ax25_setup_timers(ax25_cb *); +void ax25_start_heartbeat(ax25_cb *); +void ax25_start_t1timer(ax25_cb *); +void ax25_start_t2timer(ax25_cb *); +void ax25_start_t3timer(ax25_cb *); +void ax25_start_idletimer(ax25_cb *); +void ax25_stop_heartbeat(ax25_cb *); +void ax25_stop_t1timer(ax25_cb *); +void ax25_stop_t2timer(ax25_cb *); +void ax25_stop_t3timer(ax25_cb *); +void ax25_stop_idletimer(ax25_cb *); +int ax25_t1timer_running(ax25_cb *); +unsigned long ax25_display_timer(struct timer_list *); /* ax25_uid.c */ extern int ax25_uid_policy; -extern ax25_uid_assoc *ax25_findbyuid(kuid_t); -extern int __must_check ax25_uid_ioctl(int, struct sockaddr_ax25 *); +ax25_uid_assoc *ax25_findbyuid(kuid_t); +int __must_check ax25_uid_ioctl(int, struct sockaddr_ax25 *); extern const struct file_operations ax25_uid_fops; -extern void ax25_uid_free(void); +void ax25_uid_free(void); /* sysctl_net_ax25.c */ #ifdef CONFIG_SYSCTL -extern int ax25_register_dev_sysctl(ax25_dev *ax25_dev); -extern void ax25_unregister_dev_sysctl(ax25_dev *ax25_dev); +int ax25_register_dev_sysctl(ax25_dev *ax25_dev); +void ax25_unregister_dev_sysctl(ax25_dev *ax25_dev); #else static inline int ax25_register_dev_sysctl(ax25_dev *ax25_dev) { return 0; } static inline void ax25_unregister_dev_sysctl(ax25_dev *ax25_dev) {} -- cgit v1.2.3-71-gd317 From 10dd9b7ce37e359066b9ab59592f81684360561a Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 31 Jul 2013 17:31:37 -0700 Subject: cfg80211.h/mac80211.h: Remove extern from function prototypes There are a mix of function prototypes with and without extern in the kernel sources. Standardize on not using extern for function prototypes. Function prototypes don't need to be written with extern. extern is assumed by the compiler. Its use is as unnecessary as using auto to declare automatic/local variables in a block. Reflow modified prototypes to 80 columns. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/cfg80211.h | 34 +++++++++++++++++----------------- include/net/mac80211.h | 16 ++++++++-------- 2 files changed, 25 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7b0730aeb892..f49de283f50e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2841,7 +2841,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv); * * Return: A non-negative wiphy index or a negative error code. */ -extern int wiphy_register(struct wiphy *wiphy); +int wiphy_register(struct wiphy *wiphy); /** * wiphy_unregister - deregister a wiphy from cfg80211 @@ -2852,14 +2852,14 @@ extern int wiphy_register(struct wiphy *wiphy); * pointer, but the call may sleep to wait for an outstanding * request that is being handled. */ -extern void wiphy_unregister(struct wiphy *wiphy); +void wiphy_unregister(struct wiphy *wiphy); /** * wiphy_free - free wiphy * * @wiphy: The wiphy to free */ -extern void wiphy_free(struct wiphy *wiphy); +void wiphy_free(struct wiphy *wiphy); /* internal structs */ struct cfg80211_conn; @@ -3014,14 +3014,14 @@ static inline void *wdev_priv(struct wireless_dev *wdev) * @band: band, necessary due to channel number overlap * Return: The corresponding frequency (in MHz), or 0 if the conversion failed. */ -extern int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band); +int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band); /** * ieee80211_frequency_to_channel - convert frequency to channel number * @freq: center frequency * Return: The corresponding channel, or 0 if the conversion failed. */ -extern int ieee80211_frequency_to_channel(int freq); +int ieee80211_frequency_to_channel(int freq); /* * Name indirection necessary because the ieee80211 code also has @@ -3030,8 +3030,8 @@ extern int ieee80211_frequency_to_channel(int freq); * to include both header files you'll (rightfully!) get a symbol * clash. */ -extern struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy, - int freq); +struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy, + int freq); /** * ieee80211_get_channel - get channel struct from wiphy for specified frequency * @wiphy: the struct wiphy to get the channel for @@ -3141,13 +3141,14 @@ struct ieee80211_radiotap_iterator { int _reset_on_ext; }; -extern int ieee80211_radiotap_iterator_init( - struct ieee80211_radiotap_iterator *iterator, - struct ieee80211_radiotap_header *radiotap_header, - int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns); +int +ieee80211_radiotap_iterator_init(struct ieee80211_radiotap_iterator *iterator, + struct ieee80211_radiotap_header *radiotap_header, + int max_length, + const struct ieee80211_radiotap_vendor_namespaces *vns); -extern int ieee80211_radiotap_iterator_next( - struct ieee80211_radiotap_iterator *iterator); +int +ieee80211_radiotap_iterator_next(struct ieee80211_radiotap_iterator *iterator); extern const unsigned char rfc1042_header[6]; @@ -3307,7 +3308,7 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, * * Return: 0 on success. -ENOMEM. */ -extern int regulatory_hint(struct wiphy *wiphy, const char *alpha2); +int regulatory_hint(struct wiphy *wiphy, const char *alpha2); /** * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain @@ -3321,9 +3322,8 @@ extern int regulatory_hint(struct wiphy *wiphy, const char *alpha2); * default channel settings will be disregarded. If no rule is found for a * channel on the regulatory domain the channel will be disabled. */ -extern void wiphy_apply_custom_regulatory( - struct wiphy *wiphy, - const struct ieee80211_regdomain *regd); +void wiphy_apply_custom_regulatory(struct wiphy *wiphy, + const struct ieee80211_regdomain *regd); /** * freq_reg_info - get regulatory information for the given frequency diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 5b7a3dadadde..e5f02909f1ad 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2877,14 +2877,14 @@ enum ieee80211_tpt_led_trigger_flags { }; #ifdef CONFIG_MAC80211_LEDS -extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw); -extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw); -extern char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw); -extern char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw); -extern char *__ieee80211_create_tpt_led_trigger( - struct ieee80211_hw *hw, unsigned int flags, - const struct ieee80211_tpt_blink *blink_table, - unsigned int blink_table_len); +char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw); +char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw); +char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw); +char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw); +char *__ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw, + unsigned int flags, + const struct ieee80211_tpt_blink *blink_table, + unsigned int blink_table_len); #endif /** * ieee80211_get_tx_led_name - get name of TX LED -- cgit v1.2.3-71-gd317 From 4fc7074703321e897cc05c0de858fba63e94e59f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 31 Jul 2013 17:31:38 -0700 Subject: checksum: Remove extern from function prototypes There are a mix of function prototypes with and without extern in the kernel sources. Standardize on not using extern for function prototypes. Function prototypes don't need to be written with extern. extern is assumed by the compiler. Its use is as unnecessary as using auto to declare automatic/local variables in a block. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/checksum.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/checksum.h b/include/net/checksum.h index 600d1d705bb8..8f59ca50477c 100644 --- a/include/net/checksum.h +++ b/include/net/checksum.h @@ -107,11 +107,11 @@ static inline void csum_replace2(__sum16 *sum, __be16 from, __be16 to) } struct sk_buff; -extern void inet_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb, - __be32 from, __be32 to, int pseudohdr); -extern void inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb, - const __be32 *from, const __be32 *to, - int pseudohdr); +void inet_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb, + __be32 from, __be32 to, int pseudohdr); +void inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb, + const __be32 *from, const __be32 *to, + int pseudohdr); static inline void inet_proto_csum_replace2(__sum16 *sum, struct sk_buff *skb, __be16 from, __be16 to, -- cgit v1.2.3-71-gd317 From 378307217ed9c318212ec3050d38d0e34b77604c Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 31 Jul 2013 17:31:39 -0700 Subject: cls_cgroup.h netprio_cgroup.h: Remove extern from function prototypes There are a mix of function prototypes with and without extern in the kernel sources. Standardize on not using extern for function prototypes. Function prototypes don't need to be written with extern. extern is assumed by the compiler. Its use is as unnecessary as using auto to declare automatic/local variables in a block. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/cls_cgroup.h | 2 +- include/net/netprio_cgroup.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/cls_cgroup.h b/include/net/cls_cgroup.h index 0fee0617fb7d..f55c14510bd6 100644 --- a/include/net/cls_cgroup.h +++ b/include/net/cls_cgroup.h @@ -24,7 +24,7 @@ struct cgroup_cls_state u32 classid; }; -extern void sock_update_classid(struct sock *sk); +void sock_update_classid(struct sock *sk); #if IS_BUILTIN(CONFIG_NET_CLS_CGROUP) static inline u32 task_cls_classid(struct task_struct *p) diff --git a/include/net/netprio_cgroup.h b/include/net/netprio_cgroup.h index 50ab8c26ab59..379dd5db0f2f 100644 --- a/include/net/netprio_cgroup.h +++ b/include/net/netprio_cgroup.h @@ -29,7 +29,7 @@ struct cgroup_netprio_state { struct cgroup_subsys_state css; }; -extern void sock_update_netprioidx(struct sock *sk); +void sock_update_netprioidx(struct sock *sk); #if IS_BUILTIN(CONFIG_NETPRIO_CGROUP) -- cgit v1.2.3-71-gd317 From 674349f365ce7ac2b826edc619907a19c7136822 Mon Sep 17 00:00:00 2001 From: Andreas Robinson Date: Thu, 1 Aug 2013 10:55:34 +1000 Subject: modules: add support for soft module dependencies Additional and optional dependencies not found while building the kernel and modules, can now be declared explicitly. Signed-off-by: Andreas Robinson Acked-by: Herbert Xu Signed-off-by: Rusty Russell Signed-off-by: Herbert Xu --- include/linux/module.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/module.h b/include/linux/module.h index 46f1ea01e6f6..504035f3ece1 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -97,6 +97,11 @@ extern const struct gtype##_id __mod_##gtype##_table \ /* For userspace: you can also call me... */ #define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias) +/* Soft module dependencies. See man modprobe.d for details. + * Example: MODULE_SOFTDEP("pre: module-foo module-bar post: module-baz") + */ +#define MODULE_SOFTDEP(_softdep) MODULE_INFO(softdep, _softdep) + /* * The following license idents are currently accepted as indicating free * software modules -- cgit v1.2.3-71-gd317 From 90efa75f7ab0be5677f0cca155dbf0b39eacdd03 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Wed, 31 Jul 2013 14:56:30 +0400 Subject: serial: sccnxp: Using CLK API for getting UART clock This patch removes "frequency" parameter from SCCNXP platform_data and uses CLK API for getting clock. If CLK ommited, default IC frequency will be used instead. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- arch/mips/sni/a20r.c | 1 - drivers/tty/serial/sccnxp.c | 36 ++++++++++++++++++----------- include/linux/platform_data/serial-sccnxp.h | 3 --- 3 files changed, 22 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/arch/mips/sni/a20r.c b/arch/mips/sni/a20r.c index dd0ab982d77e..f9407e170476 100644 --- a/arch/mips/sni/a20r.c +++ b/arch/mips/sni/a20r.c @@ -122,7 +122,6 @@ static struct resource sc26xx_rsrc[] = { static struct sccnxp_pdata sccnxp_data = { .reg_shift = 2, - .frequency = 3686400, .mctrl_cfg[0] = MCTRL_SIG(DTR_OP, LINE_OP7) | MCTRL_SIG(RTS_OP, LINE_OP3) | MCTRL_SIG(DSR_IP, LINE_IP5) | diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 12a5c265f43a..81e70477e6fd 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -15,6 +15,7 @@ #define SUPPORT_SYSRQ #endif +#include #include #include #include @@ -783,9 +784,10 @@ static int sccnxp_probe(struct platform_device *pdev) struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); int chiptype = pdev->id_entry->driver_data; struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev); - int i, ret, fifosize, freq_min, freq_max; + int i, ret, fifosize, freq_min, freq_max, uartclk; struct sccnxp_port *s; void __iomem *membase; + struct clk *clk; membase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(membase)) @@ -898,11 +900,25 @@ static int sccnxp_probe(struct platform_device *pdev) } else if (PTR_ERR(s->regulator) == -EPROBE_DEFER) return -EPROBE_DEFER; - if (!pdata) { - dev_warn(&pdev->dev, - "No platform data supplied, using defaults\n"); - s->pdata.frequency = s->freq_std; + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_out; + } + dev_notice(&pdev->dev, "Using default clock frequency\n"); + uartclk = s->freq_std; } else + uartclk = clk_get_rate(clk); + + /* Check input frequency */ + if ((uartclk < freq_min) || (uartclk > freq_max)) { + dev_err(&pdev->dev, "Frequency out of bounds\n"); + ret = -EINVAL; + goto err_out; + } + + if (pdata) memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); if (s->pdata.poll_time_us) { @@ -920,14 +936,6 @@ static int sccnxp_probe(struct platform_device *pdev) } } - /* Check input frequency */ - if ((s->pdata.frequency < freq_min) || - (s->pdata.frequency > freq_max)) { - dev_err(&pdev->dev, "Frequency out of bounds\n"); - ret = -EINVAL; - goto err_out; - } - s->uart.owner = THIS_MODULE; s->uart.dev_name = "ttySC"; s->uart.major = SCCNXP_MAJOR; @@ -959,7 +967,7 @@ static int sccnxp_probe(struct platform_device *pdev) s->port[i].mapbase = res->start; s->port[i].membase = membase; s->port[i].regshift = s->pdata.reg_shift; - s->port[i].uartclk = s->pdata.frequency; + s->port[i].uartclk = uartclk; s->port[i].ops = &sccnxp_ops; uart_add_one_port(&s->uart, &s->port[i]); /* Set direction to input */ diff --git a/include/linux/platform_data/serial-sccnxp.h b/include/linux/platform_data/serial-sccnxp.h index bdc510d03245..af0c8c3b89ae 100644 --- a/include/linux/platform_data/serial-sccnxp.h +++ b/include/linux/platform_data/serial-sccnxp.h @@ -60,7 +60,6 @@ * }; * * static struct sccnxp_pdata sc2892_info = { - * .frequency = 3686400, * .mctrl_cfg[0] = MCTRL_SIG(DIR_OP, LINE_OP0), * .mctrl_cfg[1] = MCTRL_SIG(DIR_OP, LINE_OP1), * }; @@ -78,8 +77,6 @@ /* SCCNXP platform data structure */ struct sccnxp_pdata { - /* Frequency (extrenal clock or crystal) */ - int frequency; /* Shift for A0 line */ const u8 reg_shift; /* Modem control lines configuration */ -- cgit v1.2.3-71-gd317 From 85c996907473e4ef824774b97b26499adf66521f Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Wed, 31 Jul 2013 14:55:45 +0400 Subject: serial: sccnxp: Add DT support Add DT support to the SCCNCP serial driver. Signed-off-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- .../bindings/tty/serial/sccnxp-serial.txt | 53 ++++++++++++++++++++++ drivers/tty/serial/sccnxp.c | 46 +++++++++++++++---- include/linux/platform_data/serial-sccnxp.h | 6 +-- 3 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 Documentation/devicetree/bindings/tty/serial/sccnxp-serial.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/tty/serial/sccnxp-serial.txt b/Documentation/devicetree/bindings/tty/serial/sccnxp-serial.txt new file mode 100644 index 000000000000..d18b1698133e --- /dev/null +++ b/Documentation/devicetree/bindings/tty/serial/sccnxp-serial.txt @@ -0,0 +1,53 @@ +* NXP (Philips) SCC+++(SCN+++) serial driver + +Required properties: +- compatible: Should be "nxp,". The supported ICs include sc2681, + sc2691, sc2692, sc2891, sc2892, sc28202, sc68681 and sc68692. +- reg: Address and length of the register set for the device. +- interrupts: Should contain the interrupt number. If omitted, + polling mode will be used instead, so "poll-interval" property should + be populated in this case. + +Optional properties: +- clocks: Phandle to input clock. If omitted, default IC frequency will be + used instead. +- poll-interval: Poll interval time in nanoseconds. +- vcc-supply: The regulator supplying the VCC to drive the chip. +- nxp,sccnxp-io-cfg: Array contains values for the emulated modem signals. + The number of values depends on the UART-number in the selected chip. + Each value should be composed according to the following rules: + (LINE1 << SIGNAL1) | ... | (LINEX << SIGNALX), where: + LINE - VALUE: + OP0 - 1 + OP1 - 2 + OP2 - 3 + OP3 - 4 + OP4 - 5 + OP5 - 6 + OP6 - 7 + OP7 - 8 + IP0 - 9 + IP1 - 10 + IP2 - 11 + IP3 - 12 + IP4 - 13 + IP5 - 14 + IP6 - 15 + SIGNAL - VALUE: + DTR - 0 + RTS - 4 + DSR - 8 + CTS - 12 + DCD - 16 + RNG - 20 + DIR - 24 + +Example (Dual UART with direction control on OP0 & OP1): +sc2892@10100000 { + compatible = "nxp,sc2892"; + reg = <0x10100000 0x10>; + poll-interval = <10000>; + clocks = <&sc2892_clk>; + vcc-supply = <&sc2892_reg>; + nxp,sccnxp-io-cfg = <0x01000000 0x02000000>; +}; diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 49e9bbfe6cab..67f73d1a8e7b 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -853,10 +855,25 @@ static const struct platform_device_id sccnxp_id_table[] = { }; MODULE_DEVICE_TABLE(platform, sccnxp_id_table); +static const struct of_device_id sccnxp_dt_id_table[] = { + { .compatible = "nxp,sc2681", .data = &sc2681, }, + { .compatible = "nxp,sc2691", .data = &sc2691, }, + { .compatible = "nxp,sc2692", .data = &sc2692, }, + { .compatible = "nxp,sc2891", .data = &sc2891, }, + { .compatible = "nxp,sc2892", .data = &sc2892, }, + { .compatible = "nxp,sc28202", .data = &sc28202, }, + { .compatible = "nxp,sc68681", .data = &sc68681, }, + { .compatible = "nxp,sc68692", .data = &sc68692, }, + { } +}; +MODULE_DEVICE_TABLE(of, sccnxp_dt_id_table); + static int sccnxp_probe(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev); + const struct of_device_id *of_id = + of_match_device(sccnxp_dt_id_table, &pdev->dev); int i, ret, uartclk; struct sccnxp_port *s; void __iomem *membase; @@ -875,7 +892,22 @@ static int sccnxp_probe(struct platform_device *pdev) spin_lock_init(&s->lock); - s->chip = (struct sccnxp_chip *)pdev->id_entry->driver_data; + if (of_id) { + s->chip = (struct sccnxp_chip *)of_id->data; + + of_property_read_u32(pdev->dev.of_node, "poll-interval", + &s->pdata.poll_time_us); + of_property_read_u32(pdev->dev.of_node, "reg-shift", + &s->pdata.reg_shift); + of_property_read_u32_array(pdev->dev.of_node, + "nxp,sccnxp-io-cfg", + s->pdata.mctrl_cfg, s->chip->nr); + } else { + s->chip = (struct sccnxp_chip *)pdev->id_entry->driver_data; + + if (pdata) + memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); + } s->regulator = devm_regulator_get(&pdev->dev, "vcc"); if (!IS_ERR(s->regulator)) { @@ -906,16 +938,11 @@ static int sccnxp_probe(struct platform_device *pdev) goto err_out; } - if (pdata) - memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); - if (s->pdata.poll_time_us) { dev_info(&pdev->dev, "Using poll mode, resolution %u usecs\n", s->pdata.poll_time_us); s->poll = 1; - } - - if (!s->poll) { + } else { s->irq = platform_get_irq(pdev, 0); if (s->irq < 0) { dev_err(&pdev->dev, "Missing irq resource data\n"); @@ -1016,8 +1043,9 @@ static int sccnxp_remove(struct platform_device *pdev) static struct platform_driver sccnxp_uart_driver = { .driver = { - .name = SCCNXP_NAME, - .owner = THIS_MODULE, + .name = SCCNXP_NAME, + .owner = THIS_MODULE, + .of_match_table = sccnxp_dt_id_table, }, .probe = sccnxp_probe, .remove = sccnxp_remove, diff --git a/include/linux/platform_data/serial-sccnxp.h b/include/linux/platform_data/serial-sccnxp.h index af0c8c3b89ae..98373d6add27 100644 --- a/include/linux/platform_data/serial-sccnxp.h +++ b/include/linux/platform_data/serial-sccnxp.h @@ -78,11 +78,11 @@ /* SCCNXP platform data structure */ struct sccnxp_pdata { /* Shift for A0 line */ - const u8 reg_shift; + u32 reg_shift; /* Modem control lines configuration */ - const u32 mctrl_cfg[SCCNXP_MAX_UARTS]; + u32 mctrl_cfg[SCCNXP_MAX_UARTS]; /* Timer value for polling mode (usecs) */ - const unsigned int poll_time_us; + u32 poll_time_us; }; #endif -- cgit v1.2.3-71-gd317 From dcb7a6ce0a08012a17cca34711a02017abed554b Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 26 Jul 2013 17:02:29 -0700 Subject: ieee80211: add definition for interworking support IEEE802.11u interworking support is advertised via extended capabilities IE bit 31. This is 7th bit of 4th byte of extended capabilities. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b0dc87a2a376..22c909413d32 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1860,6 +1860,11 @@ enum ieee80211_tdls_actioncode { WLAN_TDLS_DISCOVERY_REQUEST = 10, }; +/* Interworking capabilities are set in 7th bit of 4th byte of the + * @WLAN_EID_EXT_CAPABILITY information element + */ +#define WLAN_EXT_CAPA4_INTERWORKING_ENABLED BIT(7) + /* * TDLS capabililites to be enabled in the 5th byte of the * @WLAN_EID_EXT_CAPABILITY information element -- cgit v1.2.3-71-gd317 From 9e2bc79bce58a1ce0005015c9351b3bcaaa02e5c Mon Sep 17 00:00:00 2001 From: Fred Zhou Date: Thu, 1 Aug 2013 14:16:28 +0800 Subject: ieee80211: add definition for 802.11ac information elements Add element IDs for Extended BSS Load, VHT TX Power Envelope, AID, and Quiet Channel. Signed-off-by: Fred Zhou Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 22c909413d32..b3ce299782af 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1709,6 +1709,10 @@ enum ieee80211_eid { WLAN_EID_OPMODE_NOTIF = 199, WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194, WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196, + WLAN_EID_EXTENDED_BSS_LOAD = 193, + WLAN_EID_VHT_TX_POWER_ENVELOPE = 195, + WLAN_EID_AID = 197, + WLAN_EID_QUIET_CHANNEL = 198, /* 802.11ad */ WLAN_EID_NON_TX_BSSID_CAP = 83, -- cgit v1.2.3-71-gd317 From 16ef1fe272332b2f7fd99236017b891db48d9cd6 Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Thu, 11 Jul 2013 16:09:05 +0200 Subject: nl80211/cfg80211: add channel switch command To allow channel switch announcements within beacons, add the channel switch command to nl80211/cfg80211. This is implementation is intended for AP and (later) IBSS mode. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 33 ++++++++++++ include/uapi/linux/nl80211.h | 30 +++++++++++ net/wireless/nl80211.c | 122 ++++++++++++++++++++++++++++++++++++++++++- net/wireless/rdev-ops.h | 12 +++++ net/wireless/trace.h | 33 ++++++++++++ 5 files changed, 229 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index aeaf6dff6e05..b7495c72061c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -665,6 +665,30 @@ struct cfg80211_ap_settings { bool radar_required; }; +/** + * struct cfg80211_csa_settings - channel switch settings + * + * Used for channel switch + * + * @chandef: defines the channel to use after the switch + * @beacon_csa: beacon data while performing the switch + * @counter_offset_beacon: offset for the counter within the beacon (tail) + * @counter_offset_presp: offset for the counter within the probe response + * @beacon_after: beacon data to be used on the new channel + * @radar_required: whether radar detection is required on the new channel + * @block_tx: whether transmissions should be blocked while changing + * @count: number of beacons until switch + */ +struct cfg80211_csa_settings { + struct cfg80211_chan_def chandef; + struct cfg80211_beacon_data beacon_csa; + u16 counter_offset_beacon, counter_offset_presp; + struct cfg80211_beacon_data beacon_after; + bool radar_required; + bool block_tx; + u8 count; +}; + /** * enum station_parameters_apply_mask - station parameter values to apply * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp) @@ -2139,6 +2163,8 @@ struct cfg80211_update_ft_ies_params { * @crit_proto_stop: Indicates critical protocol no longer needs increased link * reliability. This operation can not fail. * @set_coalesce: Set coalesce parameters. + * + * @channel_switch: initiate channel-switch procedure (with CSA) */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -2376,6 +2402,10 @@ struct cfg80211_ops { struct wireless_dev *wdev); int (*set_coalesce)(struct wiphy *wiphy, struct cfg80211_coalesce *coalesce); + + int (*channel_switch)(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_csa_settings *params); }; /* @@ -2441,6 +2471,8 @@ struct cfg80211_ops { * @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX. * @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call. * @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels. + * @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in + * beaconing mode (AP, IBSS, Mesh, ...). */ enum wiphy_flags { WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), @@ -2465,6 +2497,7 @@ enum wiphy_flags { WIPHY_FLAG_OFFCHAN_TX = BIT(20), WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL = BIT(21), WIPHY_FLAG_SUPPORTS_5_10_MHZ = BIT(22), + WIPHY_FLAG_HAS_CHANNEL_SWITCH = BIT(23), }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index eb68735b3318..1f42bc3dcb9c 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -676,6 +676,16 @@ * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules. * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules. * + * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the + * the new channel information (Channel Switch Announcement - CSA) + * in the beacon for some time (as defined in the + * %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the + * new channel. Userspace provides the new channel information (using + * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel + * width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform + * other station that transmission must be blocked until the channel + * switch is complete. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -841,6 +851,8 @@ enum nl80211_commands { NL80211_CMD_GET_COALESCE, NL80211_CMD_SET_COALESCE, + NL80211_CMD_CHANNEL_SWITCH, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1469,6 +1481,18 @@ enum nl80211_commands { * * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information. * + * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's + * until the channel switch event. + * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission + * must be blocked on the current channel (before the channel switch + * operation). + * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information + * for the time while performing a channel switch. + * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter + * field in the beacons tail (%NL80211_ATTR_BEACON_TAIL). + * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter + * field in the probe response (%NL80211_ATTR_PROBE_RESP). + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1771,6 +1795,12 @@ enum nl80211_attrs { NL80211_ATTR_COALESCE_RULE, + NL80211_ATTR_CH_SWITCH_COUNT, + NL80211_ATTR_CH_SWITCH_BLOCK_TX, + NL80211_ATTR_CSA_IES, + NL80211_ATTR_CSA_C_OFF_BEACON, + NL80211_ATTR_CSA_C_OFF_PRESP, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 03d4ef95292e..f7cb12178bd2 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -349,6 +349,11 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_PEER_AID] = { .type = NLA_U16 }, + [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 }, + [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG }, + [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED }, + [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 }, + [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 }, }; /* policy for the key attributes */ @@ -1422,6 +1427,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, if (state->split) { CMD(crit_proto_start, CRIT_PROTOCOL_START); CMD(crit_proto_stop, CRIT_PROTOCOL_STOP); + if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) + CMD(channel_switch, CHANNEL_SWITCH); } #ifdef CONFIG_NL80211_TESTMODE @@ -5613,6 +5620,111 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, return err; } +static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_csa_settings params; + /* csa_attrs is defined static to avoid waste of stack size - this + * function is called under RTNL lock, so this should not be a problem. + */ + static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1]; + u8 radar_detect_width = 0; + int err; + + if (!rdev->ops->channel_switch || + !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)) + return -EOPNOTSUPP; + + /* may add IBSS support later */ + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + memset(¶ms, 0, sizeof(params)); + + if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || + !info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]) + return -EINVAL; + + /* only important for AP, IBSS and mesh create IEs internally */ + if (!info->attrs[NL80211_ATTR_CSA_IES]) + return -EINVAL; + + /* useless if AP is not running */ + if (!wdev->beacon_interval) + return -EINVAL; + + params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]); + + err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after); + if (err) + return err; + + err = nla_parse_nested(csa_attrs, NL80211_ATTR_MAX, + info->attrs[NL80211_ATTR_CSA_IES], + nl80211_policy); + if (err) + return err; + + err = nl80211_parse_beacon(csa_attrs, ¶ms.beacon_csa); + if (err) + return err; + + if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]) + return -EINVAL; + + params.counter_offset_beacon = + nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]); + if (params.counter_offset_beacon >= params.beacon_csa.tail_len) + return -EINVAL; + + /* sanity check - counters should be the same */ + if (params.beacon_csa.tail[params.counter_offset_beacon] != + params.count) + return -EINVAL; + + if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) { + params.counter_offset_presp = + nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]); + if (params.counter_offset_presp >= + params.beacon_csa.probe_resp_len) + return -EINVAL; + + if (params.beacon_csa.probe_resp[params.counter_offset_presp] != + params.count) + return -EINVAL; + } + + err = nl80211_parse_chandef(rdev, info, ¶ms.chandef); + if (err) + return err; + + if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef)) + return -EINVAL; + + err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef); + if (err < 0) { + return err; + } else if (err) { + radar_detect_width = BIT(params.chandef.width); + params.radar_required = true; + } + + err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, + params.chandef.chan, + CHAN_MODE_SHARED, + radar_detect_width); + if (err) + return err; + + if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX]) + params.block_tx = true; + + return rdev_channel_switch(rdev, dev, ¶ms); +} + static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, u32 seq, int flags, struct cfg80211_registered_device *rdev, @@ -9361,7 +9473,15 @@ static struct genl_ops nl80211_ops[] = { .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WIPHY | NL80211_FLAG_NEED_RTNL, - } + }, + { + .cmd = NL80211_CMD_CHANNEL_SWITCH, + .doit = nl80211_channel_switch, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 9f15f0ac824d..de870d4d0bcc 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -923,4 +923,16 @@ static inline void rdev_crit_proto_stop(struct cfg80211_registered_device *rdev, trace_rdev_return_void(&rdev->wiphy); } +static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_csa_settings *params) +{ + int ret; + + trace_rdev_channel_switch(&rdev->wiphy, dev, params); + ret = rdev->ops->channel_switch(&rdev->wiphy, dev, params); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 09af6eb426a8..f0ebdcd394ef 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1841,6 +1841,39 @@ TRACE_EVENT(rdev_crit_proto_stop, WIPHY_PR_ARG, WDEV_PR_ARG) ); +TRACE_EVENT(rdev_channel_switch, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct cfg80211_csa_settings *params), + TP_ARGS(wiphy, netdev, params), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + CHAN_DEF_ENTRY + __field(u16, counter_offset_beacon) + __field(u16, counter_offset_presp) + __field(bool, radar_required) + __field(bool, block_tx) + __field(u8, count) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + CHAN_DEF_ASSIGN(¶ms->chandef); + __entry->counter_offset_beacon = params->counter_offset_beacon; + __entry->counter_offset_presp = params->counter_offset_presp; + __entry->radar_required = params->radar_required; + __entry->block_tx = params->block_tx; + __entry->count = params->count; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT + ", block_tx: %d, count: %u, radar_required: %d" + ", counter offsets (beacon/presp): %u/%u", + WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG, + __entry->block_tx, __entry->count, __entry->radar_required, + __entry->counter_offset_beacon, + __entry->counter_offset_presp) +); + /************************************************************* * cfg80211 exported functions traces * *************************************************************/ -- cgit v1.2.3-71-gd317 From 73da7d5bab79ad7e16ff44d67c3fe8b9c0b33e5b Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Thu, 11 Jul 2013 16:09:06 +0200 Subject: mac80211: add channel switch command and beacon callbacks The count field in CSA must be decremented with each beacon transmitted. This patch implements the functionality for drivers using ieee80211_beacon_get(). Other drivers must call back manually after reaching count == 0. This patch also contains the handling and finish worker for the channel switch command, and mac80211/chanctx code to allow to change a channel definition of an active channel context. Signed-off-by: Simon Wunderlich Signed-off-by: Mathias Kretschmer [small cleanups, catch identical chandef] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 37 +++++++++ net/mac80211/cfg.c | 187 ++++++++++++++++++++++++++++++++++++++++++++- net/mac80211/chan.c | 58 ++++++++++++++ net/mac80211/driver-ops.h | 13 ++++ net/mac80211/ieee80211_i.h | 17 +++++ net/mac80211/iface.c | 9 +++ net/mac80211/trace.h | 26 +++++++ net/mac80211/tx.c | 78 +++++++++++++++++++ 8 files changed, 423 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3124036285eb..9cda3728c2cb 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -152,11 +152,14 @@ struct ieee80211_low_level_stats { * @IEEE80211_CHANCTX_CHANGE_WIDTH: The channel width changed * @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed * @IEEE80211_CHANCTX_CHANGE_RADAR: radar detection flag changed + * @IEEE80211_CHANCTX_CHANGE_CHANNEL: switched to another operating channel, + * this is used only with channel switching with CSA */ enum ieee80211_chanctx_change { IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0), IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(1), IEEE80211_CHANCTX_CHANGE_RADAR = BIT(2), + IEEE80211_CHANCTX_CHANGE_CHANNEL = BIT(3), }; /** @@ -1084,6 +1087,7 @@ enum ieee80211_vif_flags { * @addr: address of this interface * @p2p: indicates whether this AP or STA interface is a p2p * interface, i.e. a GO or p2p-sta respectively + * @csa_active: marks whether a channel switch is going on * @driver_flags: flags/capabilities the driver has for this interface, * these need to be set (or cleared) when the interface is added * or, if supported by the driver, the interface type is changed @@ -1106,6 +1110,7 @@ struct ieee80211_vif { struct ieee80211_bss_conf bss_conf; u8 addr[ETH_ALEN]; bool p2p; + bool csa_active; u8 cab_queue; u8 hw_queue[IEEE80211_NUM_ACS]; @@ -2637,6 +2642,16 @@ enum ieee80211_roc_type { * @ipv6_addr_change: IPv6 address assignment on the given interface changed. * Currently, this is only called for managed or P2P client interfaces. * This callback is optional; it must not sleep. + * + * @channel_switch_beacon: Starts a channel switch to a new channel. + * Beacons are modified to include CSA or ECSA IEs before calling this + * function. The corresponding count fields in these IEs must be + * decremented, and when they reach zero the driver must call + * ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get() + * get the csa counter decremented by mac80211, but must check if it is + * zero using ieee80211_csa_is_complete() after the beacon has been + * transmitted and then call ieee80211_csa_finish(). + * */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, @@ -2824,6 +2839,9 @@ struct ieee80211_ops { struct ieee80211_vif *vif, struct inet6_dev *idev); #endif + void (*channel_switch_beacon)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef); }; /** @@ -3318,6 +3336,25 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, return ieee80211_beacon_get_tim(hw, vif, NULL, NULL); } +/** + * ieee80211_csa_finish - notify mac80211 about channel switch + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * After a channel switch announcement was scheduled and the counter in this + * announcement hit zero, this function must be called by the driver to + * notify mac80211 that the channel can be changed. + */ +void ieee80211_csa_finish(struct ieee80211_vif *vif); + +/** + * ieee80211_csa_is_complete - find out if counters reached zero + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * This function returns whether the channel switch counters reached zero. + */ +bool ieee80211_csa_is_complete(struct ieee80211_vif *vif); + + /** * ieee80211_proberesp_get - retrieve a Probe Response template * @hw: pointer obtained from ieee80211_alloc_hw(). diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b82fff6c0b30..44449ceb7966 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -860,8 +860,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, return 0; } -static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, - struct cfg80211_beacon_data *params) +int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_beacon_data *params) { struct beacon_data *new, *old; int new_head_len, new_tail_len; @@ -1024,6 +1024,12 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, sdata = IEEE80211_DEV_TO_SUB_IF(dev); + /* don't allow changing the beacon while CSA is in place - offset + * of channel switch counter may change + */ + if (sdata->vif.csa_active) + return -EBUSY; + old = rtnl_dereference(sdata->u.ap.beacon); if (!old) return -ENOENT; @@ -1048,6 +1054,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) return -ENOENT; old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp); + /* abort any running channel switch */ + sdata->vif.csa_active = false; + cancel_work_sync(&sdata->csa_finalize_work); + /* turn off carrier for this interface and dependent VLANs */ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) netif_carrier_off(vlan->dev); @@ -2775,6 +2785,178 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy, return 0; } +static struct cfg80211_beacon_data * +cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon) +{ + struct cfg80211_beacon_data *new_beacon; + u8 *pos; + int len; + + len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len + + beacon->proberesp_ies_len + beacon->assocresp_ies_len + + beacon->probe_resp_len; + + new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL); + if (!new_beacon) + return NULL; + + pos = (u8 *)(new_beacon + 1); + if (beacon->head_len) { + new_beacon->head_len = beacon->head_len; + new_beacon->head = pos; + memcpy(pos, beacon->head, beacon->head_len); + pos += beacon->head_len; + } + if (beacon->tail_len) { + new_beacon->tail_len = beacon->tail_len; + new_beacon->tail = pos; + memcpy(pos, beacon->tail, beacon->tail_len); + pos += beacon->tail_len; + } + if (beacon->beacon_ies_len) { + new_beacon->beacon_ies_len = beacon->beacon_ies_len; + new_beacon->beacon_ies = pos; + memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len); + pos += beacon->beacon_ies_len; + } + if (beacon->proberesp_ies_len) { + new_beacon->proberesp_ies_len = beacon->proberesp_ies_len; + new_beacon->proberesp_ies = pos; + memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len); + pos += beacon->proberesp_ies_len; + } + if (beacon->assocresp_ies_len) { + new_beacon->assocresp_ies_len = beacon->assocresp_ies_len; + new_beacon->assocresp_ies = pos; + memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len); + pos += beacon->assocresp_ies_len; + } + if (beacon->probe_resp_len) { + new_beacon->probe_resp_len = beacon->probe_resp_len; + beacon->probe_resp = pos; + memcpy(pos, beacon->probe_resp, beacon->probe_resp_len); + pos += beacon->probe_resp_len; + } + + return new_beacon; +} + +void ieee80211_csa_finalize_work(struct work_struct *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + csa_finalize_work); + struct ieee80211_local *local = sdata->local; + int err, changed; + + if (!ieee80211_sdata_running(sdata)) + return; + + if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) + return; + + sdata->radar_required = sdata->csa_radar_required; + err = ieee80211_vif_change_channel(sdata, &local->csa_chandef, + &changed); + if (WARN_ON(err < 0)) + return; + + err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); + if (err < 0) + return; + + changed |= err; + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; + sdata->vif.csa_active = false; + + ieee80211_wake_queues_by_reason(&sdata->local->hw, + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); + + ieee80211_bss_info_change_notify(sdata, changed); + + cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef); +} + +static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_chanctx *chanctx; + int err, num_chanctx; + + if (!list_empty(&local->roc_list) || local->scanning) + return -EBUSY; + + if (sdata->wdev.cac_started) + return -EBUSY; + + if (cfg80211_chandef_identical(¶ms->chandef, + &sdata->vif.bss_conf.chandef)) + return -EINVAL; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) { + rcu_read_unlock(); + return -EBUSY; + } + + /* don't handle for multi-VIF cases */ + chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); + if (chanctx->refcount > 1) { + rcu_read_unlock(); + return -EBUSY; + } + num_chanctx = 0; + list_for_each_entry_rcu(chanctx, &local->chanctx_list, list) + num_chanctx++; + rcu_read_unlock(); + + if (num_chanctx > 1) + return -EBUSY; + + /* don't allow another channel switch if one is already active. */ + if (sdata->vif.csa_active) + return -EBUSY; + + /* only handle AP for now. */ + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP: + break; + default: + return -EOPNOTSUPP; + } + + sdata->u.ap.next_beacon = cfg80211_beacon_dup(¶ms->beacon_after); + if (!sdata->u.ap.next_beacon) + return -ENOMEM; + + sdata->csa_counter_offset_beacon = params->counter_offset_beacon; + sdata->csa_counter_offset_presp = params->counter_offset_presp; + sdata->csa_radar_required = params->radar_required; + + if (params->block_tx) + ieee80211_stop_queues_by_reason(&local->hw, + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); + + err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); + if (err < 0) + return err; + + local->csa_chandef = params->chandef; + sdata->vif.csa_active = true; + + ieee80211_bss_info_change_notify(sdata, err); + drv_channel_switch_beacon(sdata, ¶ms->chandef); + + return 0; +} + static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, unsigned int wait, const u8 *buf, size_t len, @@ -3492,4 +3674,5 @@ struct cfg80211_ops mac80211_config_ops = { .get_et_strings = ieee80211_get_et_strings, .get_channel = ieee80211_cfg_get_channel, .start_radar_detection = ieee80211_start_radar_detection, + .channel_switch = ieee80211_channel_switch, }; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 03e8d2e3270e..3a4764b2869e 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -410,6 +410,64 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, return ret; } +int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, + const struct cfg80211_chan_def *chandef, + u32 *changed) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *conf; + struct ieee80211_chanctx *ctx; + int ret; + u32 chanctx_changed = 0; + + /* should never be called if not performing a channel switch. */ + if (WARN_ON(!sdata->vif.csa_active)) + return -EINVAL; + + if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, + IEEE80211_CHAN_DISABLED)) + return -EINVAL; + + mutex_lock(&local->chanctx_mtx); + conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + if (!conf) { + ret = -EINVAL; + goto out; + } + + ctx = container_of(conf, struct ieee80211_chanctx, conf); + if (ctx->refcount != 1) { + ret = -EINVAL; + goto out; + } + + if (sdata->vif.bss_conf.chandef.width != chandef->width) { + chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH; + *changed |= BSS_CHANGED_BANDWIDTH; + } + + sdata->vif.bss_conf.chandef = *chandef; + ctx->conf.def = *chandef; + + chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL; + drv_change_chanctx(local, ctx, chanctx_changed); + + if (!local->use_chanctx) { + local->_oper_chandef = *chandef; + ieee80211_hw_config(local, 0); + } + + ieee80211_recalc_chanctx_chantype(local, ctx); + ieee80211_recalc_smps_chanctx(local, ctx); + ieee80211_recalc_radar_chanctx(local, ctx); + + ret = 0; + out: + mutex_unlock(&local->chanctx_mtx); + return ret; +} + int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, const struct cfg80211_chan_def *chandef, u32 *changed) diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index b931c96a596f..b3ea11f3d526 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1072,4 +1072,17 @@ static inline void drv_ipv6_addr_change(struct ieee80211_local *local, } #endif +static inline void +drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_chan_def *chandef) +{ + struct ieee80211_local *local = sdata->local; + + if (local->ops->channel_switch_beacon) { + trace_drv_channel_switch_beacon(local, sdata, chandef); + local->ops->channel_switch_beacon(&local->hw, &sdata->vif, + chandef); + } +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index d779383c52d2..e94c84050e9c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -259,6 +259,8 @@ struct ieee80211_if_ap { struct beacon_data __rcu *beacon; struct probe_resp __rcu *probe_resp; + /* to be used after channel switch. */ + struct cfg80211_beacon_data *next_beacon; struct list_head vlans; struct ps_data ps; @@ -716,6 +718,11 @@ struct ieee80211_sub_if_data { struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; + struct work_struct csa_finalize_work; + int csa_counter_offset_beacon; + int csa_counter_offset_presp; + bool csa_radar_required; + /* used to reconfigure hardware SM PS */ struct work_struct recalc_smps; @@ -1372,6 +1379,9 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free); void ieee80211_sw_roc_work(struct work_struct *work); void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); +/* channel switch handling */ +void ieee80211_csa_finalize_work(struct work_struct *work); + /* interface handling */ int ieee80211_iface_init(void); void ieee80211_iface_exit(void); @@ -1393,6 +1403,8 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local); bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); +int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_beacon_data *params); static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) { @@ -1654,6 +1666,11 @@ int __must_check ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, const struct cfg80211_chan_def *chandef, u32 *changed); +/* NOTE: only use ieee80211_vif_change_channel() for channel switch */ +int __must_check +ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, + const struct cfg80211_chan_def *chandef, + u32 *changed); void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata); void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 4c41c11958c8..7ca534bf4cea 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -274,6 +274,12 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata, if (iftype == NL80211_IFTYPE_ADHOC && nsdata->vif.type == NL80211_IFTYPE_ADHOC) return -EBUSY; + /* + * will not add another interface while any channel + * switch is active. + */ + if (nsdata->vif.csa_active) + return -EBUSY; /* * The remaining checks are only performed for interfaces @@ -804,6 +810,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&local->dynamic_ps_enable_work); cancel_work_sync(&sdata->recalc_smps); + sdata->vif.csa_active = false; + cancel_work_sync(&sdata->csa_finalize_work); cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); @@ -1267,6 +1275,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, skb_queue_head_init(&sdata->skb_queue); INIT_WORK(&sdata->work, ieee80211_iface_work); INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work); + INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work); switch (type) { case NL80211_IFTYPE_P2P_GO: diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index c215fafd7a2f..1aba645882bd 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1906,6 +1906,32 @@ TRACE_EVENT(api_radar_detected, ) ); +TRACE_EVENT(drv_channel_switch_beacon, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct cfg80211_chan_def *chandef), + + TP_ARGS(local, sdata, chandef), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + CHANDEF_ENTRY + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + CHANDEF_ASSIGN(chandef); + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " channel switch to " CHANDEF_PR_FMT, + LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG + ) +); + + #ifdef CONFIG_MAC80211_MESSAGE_TRACING #undef TRACE_SYSTEM #define TRACE_SYSTEM mac80211_msg diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index f65873f0c89f..0e42322aa6b1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2338,6 +2338,81 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, return 0; } +void ieee80211_csa_finish(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + ieee80211_queue_work(&sdata->local->hw, + &sdata->csa_finalize_work); +} +EXPORT_SYMBOL(ieee80211_csa_finish); + +static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata, + struct beacon_data *beacon) +{ + struct probe_resp *resp; + int counter_offset_beacon = sdata->csa_counter_offset_beacon; + int counter_offset_presp = sdata->csa_counter_offset_presp; + + /* warn if the driver did not check for/react to csa completeness */ + if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0)) + return; + + ((u8 *)beacon->tail)[counter_offset_beacon]--; + + if (sdata->vif.type == NL80211_IFTYPE_AP && + counter_offset_presp) { + rcu_read_lock(); + resp = rcu_dereference(sdata->u.ap.probe_resp); + + /* if nl80211 accepted the offset, this should not happen. */ + if (WARN_ON(!resp)) { + rcu_read_unlock(); + return; + } + resp->data[counter_offset_presp]--; + rcu_read_unlock(); + } +} + +bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct beacon_data *beacon = NULL; + u8 *beacon_data; + size_t beacon_data_len; + int counter_beacon = sdata->csa_counter_offset_beacon; + int ret = false; + + if (!ieee80211_sdata_running(sdata)) + return false; + + rcu_read_lock(); + if (vif->type == NL80211_IFTYPE_AP) { + struct ieee80211_if_ap *ap = &sdata->u.ap; + + beacon = rcu_dereference(ap->beacon); + if (WARN_ON(!beacon || !beacon->tail)) + goto out; + beacon_data = beacon->tail; + beacon_data_len = beacon->tail_len; + } else { + WARN_ON(1); + goto out; + } + + if (WARN_ON(counter_beacon > beacon_data_len)) + goto out; + + if (beacon_data[counter_beacon] == 0) + ret = true; + out: + rcu_read_unlock(); + + return ret; +} +EXPORT_SYMBOL(ieee80211_csa_is_complete); + struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 *tim_offset, u16 *tim_length) @@ -2368,6 +2443,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, struct beacon_data *beacon = rcu_dereference(ap->beacon); if (beacon) { + if (sdata->vif.csa_active) + ieee80211_update_csa(sdata, beacon); + /* * headroom, head length, * tail length and maximum TIM length -- cgit v1.2.3-71-gd317 From 376c7311bdb6efea3322310333576a04d73fbe4c Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 1 Aug 2013 11:43:08 -0700 Subject: net: add a temporary sanity check in skb_orphan() David suggested to add a BUG_ON() to catch if some layer sets skb->sk pointer without a corresponding destructor. As skb can sit in a queue, it's mandatory to make sure the socket cannot disappear, and it's usually done by taking a reference on the socket, then releasing it from the skb destructor. This patch is a follow-up to commit c34a761231b5 ("net: skb_orphan() changes") and will be reverted after catching all possible offenders if any. Suggested-by: David Miller Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index a95547adff25..d48de687c21f 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1809,6 +1809,8 @@ static inline void skb_orphan(struct sk_buff *skb) skb->destructor(skb); skb->destructor = NULL; skb->sk = NULL; + } else { + BUG_ON(skb->sk); } } -- cgit v1.2.3-71-gd317 From cfc25993e81f3fa68481d062be634d33184d5eae Mon Sep 17 00:00:00 2001 From: Olaf Hering Date: Wed, 29 May 2013 11:29:07 +0200 Subject: Drivers: hv: remove HV_DRV_VERSION Remove HV_DRV_VERSION, it has no meaning for upstream drivers. Initially it was supposed to show the "Linux Integration Services" version, now it is not in sync anymore with the out-of-tree drivers available from the MSFT website. The only place where a version string is still required is the KVP command "IntegrationServicesVersion" which is handled by tools/hv/hv_kvp_daemon.c. To satisfy such KVP request from the host pass the current string to the daemon during KVP userland registration. Signed-off-by: Olaf Hering Acked-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-hyperv.c | 1 - drivers/hv/hv_balloon.c | 1 - drivers/hv/hv_kvp.c | 2 ++ drivers/hv/hv_util.c | 1 - drivers/hv/vmbus_drv.c | 1 - drivers/net/hyperv/netvsc_drv.c | 2 -- drivers/scsi/storvsc_drv.c | 1 - drivers/video/hyperv_fb.c | 1 - include/linux/hyperv.h | 21 --------------------- 9 files changed, 2 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index 713217380b44..8fae6d1414cc 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -590,6 +590,5 @@ static void __exit mousevsc_exit(void) } MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); module_init(mousevsc_init); module_exit(mousevsc_exit); diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 2d094cfdb1e0..7e17a5495e02 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1526,5 +1526,4 @@ static int __init init_balloon_drv(void) module_init(init_balloon_drv); MODULE_DESCRIPTION("Hyper-V Balloon"); -MODULE_VERSION(HV_DRV_VERSION); MODULE_LICENSE("GPL"); diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 53127209a404..28b03325b872 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -86,7 +86,9 @@ static u8 *recv_buffer; /* * Register the kernel component with the user-level daemon. * As part of this registration, pass the LIC version number. + * This number has no meaning, it satisfies the registration protocol. */ +#define HV_DRV_VERSION "3.1" static void kvp_register(int reg_value) diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index c16164d4a28b..cb82233541b1 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -395,5 +395,4 @@ module_init(init_hyperv_utils); module_exit(exit_hyperv_utils); MODULE_DESCRIPTION("Hyper-V Utilities"); -MODULE_VERSION(HV_DRV_VERSION); MODULE_LICENSE("GPL"); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index e8e071fc1d6d..f9fe46f52cfa 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -816,7 +816,6 @@ static void __exit vmbus_exit(void) MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); subsys_initcall(hv_acpi_init); module_exit(vmbus_exit); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 23a0fff0df52..524f713f6017 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -306,7 +306,6 @@ static void netvsc_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) { strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - strlcpy(info->version, HV_DRV_VERSION, sizeof(info->version)); strlcpy(info->fw_version, "N/A", sizeof(info->fw_version)); } @@ -529,7 +528,6 @@ static int __init netvsc_drv_init(void) } MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); MODULE_DESCRIPTION("Microsoft Hyper-V network driver"); module_init(netvsc_drv_init); diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 83ec1aa85964..1a28f5632797 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1879,7 +1879,6 @@ static void __exit storvsc_drv_exit(void) } MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); MODULE_DESCRIPTION("Microsoft Hyper-V virtual storage driver"); module_init(storvsc_drv_init); module_exit(storvsc_drv_exit); diff --git a/drivers/video/hyperv_fb.c b/drivers/video/hyperv_fb.c index d4d2c5fe2488..8ac99b87c07e 100644 --- a/drivers/video/hyperv_fb.c +++ b/drivers/video/hyperv_fb.c @@ -825,5 +825,4 @@ module_init(hvfb_drv_init); module_exit(hvfb_drv_exit); MODULE_LICENSE("GPL"); -MODULE_VERSION(HV_DRV_VERSION); MODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Video Frame Buffer Driver"); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 49949079cf47..a3b8b2e2d244 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -463,27 +463,6 @@ hv_get_ringbuffer_availbytes(struct hv_ring_buffer_info *rbi, *read = dsize - *write; } - -/* - * We use the same version numbering for all Hyper-V modules. - * - * Definition of versioning is as follows; - * - * Major Number Changes for these scenarios; - * 1. When a new version of Windows Hyper-V - * is released. - * 2. A Major change has occurred in the - * Linux IC's. - * (For example the merge for the first time - * into the kernel) Every time the Major Number - * changes, the Revision number is reset to 0. - * Minor Number Changes when new functionality is added - * to the Linux IC's that is not a bug fix. - * - * 3.1 - Added completed hv_utils driver. Shutdown/Heartbeat/Timesync - */ -#define HV_DRV_VERSION "3.1" - /* * VMBUS version is 32 bit entity broken up into * two 16 bit quantities: major_number. minor_number. -- cgit v1.2.3-71-gd317 From 461a8ecb2d76fb6f3ac1a90eb886697db869d387 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 2 Aug 2013 15:25:19 +0800 Subject: Revert "serial: sccnxp: Add DT support" This reverts commit 85c996907473e4ef824774b97b26499adf66521f. Alexander wishes to remove this patch as it is incorrect. Reported-by: Alexander Shiyan Signed-off-by: Greg Kroah-Hartman --- .../bindings/tty/serial/sccnxp-serial.txt | 53 ---------------------- drivers/tty/serial/sccnxp.c | 46 ++++--------------- include/linux/platform_data/serial-sccnxp.h | 6 +-- 3 files changed, 12 insertions(+), 93 deletions(-) delete mode 100644 Documentation/devicetree/bindings/tty/serial/sccnxp-serial.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/tty/serial/sccnxp-serial.txt b/Documentation/devicetree/bindings/tty/serial/sccnxp-serial.txt deleted file mode 100644 index d18b1698133e..000000000000 --- a/Documentation/devicetree/bindings/tty/serial/sccnxp-serial.txt +++ /dev/null @@ -1,53 +0,0 @@ -* NXP (Philips) SCC+++(SCN+++) serial driver - -Required properties: -- compatible: Should be "nxp,". The supported ICs include sc2681, - sc2691, sc2692, sc2891, sc2892, sc28202, sc68681 and sc68692. -- reg: Address and length of the register set for the device. -- interrupts: Should contain the interrupt number. If omitted, - polling mode will be used instead, so "poll-interval" property should - be populated in this case. - -Optional properties: -- clocks: Phandle to input clock. If omitted, default IC frequency will be - used instead. -- poll-interval: Poll interval time in nanoseconds. -- vcc-supply: The regulator supplying the VCC to drive the chip. -- nxp,sccnxp-io-cfg: Array contains values for the emulated modem signals. - The number of values depends on the UART-number in the selected chip. - Each value should be composed according to the following rules: - (LINE1 << SIGNAL1) | ... | (LINEX << SIGNALX), where: - LINE - VALUE: - OP0 - 1 - OP1 - 2 - OP2 - 3 - OP3 - 4 - OP4 - 5 - OP5 - 6 - OP6 - 7 - OP7 - 8 - IP0 - 9 - IP1 - 10 - IP2 - 11 - IP3 - 12 - IP4 - 13 - IP5 - 14 - IP6 - 15 - SIGNAL - VALUE: - DTR - 0 - RTS - 4 - DSR - 8 - CTS - 12 - DCD - 16 - RNG - 20 - DIR - 24 - -Example (Dual UART with direction control on OP0 & OP1): -sc2892@10100000 { - compatible = "nxp,sc2892"; - reg = <0x10100000 0x10>; - poll-interval = <10000>; - clocks = <&sc2892_clk>; - vcc-supply = <&sc2892_reg>; - nxp,sccnxp-io-cfg = <0x01000000 0x02000000>; -}; diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 67f73d1a8e7b..49e9bbfe6cab 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -20,8 +20,6 @@ #include #include #include -#include -#include #include #include #include @@ -855,25 +853,10 @@ static const struct platform_device_id sccnxp_id_table[] = { }; MODULE_DEVICE_TABLE(platform, sccnxp_id_table); -static const struct of_device_id sccnxp_dt_id_table[] = { - { .compatible = "nxp,sc2681", .data = &sc2681, }, - { .compatible = "nxp,sc2691", .data = &sc2691, }, - { .compatible = "nxp,sc2692", .data = &sc2692, }, - { .compatible = "nxp,sc2891", .data = &sc2891, }, - { .compatible = "nxp,sc2892", .data = &sc2892, }, - { .compatible = "nxp,sc28202", .data = &sc28202, }, - { .compatible = "nxp,sc68681", .data = &sc68681, }, - { .compatible = "nxp,sc68692", .data = &sc68692, }, - { } -}; -MODULE_DEVICE_TABLE(of, sccnxp_dt_id_table); - static int sccnxp_probe(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct sccnxp_pdata *pdata = dev_get_platdata(&pdev->dev); - const struct of_device_id *of_id = - of_match_device(sccnxp_dt_id_table, &pdev->dev); int i, ret, uartclk; struct sccnxp_port *s; void __iomem *membase; @@ -892,22 +875,7 @@ static int sccnxp_probe(struct platform_device *pdev) spin_lock_init(&s->lock); - if (of_id) { - s->chip = (struct sccnxp_chip *)of_id->data; - - of_property_read_u32(pdev->dev.of_node, "poll-interval", - &s->pdata.poll_time_us); - of_property_read_u32(pdev->dev.of_node, "reg-shift", - &s->pdata.reg_shift); - of_property_read_u32_array(pdev->dev.of_node, - "nxp,sccnxp-io-cfg", - s->pdata.mctrl_cfg, s->chip->nr); - } else { - s->chip = (struct sccnxp_chip *)pdev->id_entry->driver_data; - - if (pdata) - memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); - } + s->chip = (struct sccnxp_chip *)pdev->id_entry->driver_data; s->regulator = devm_regulator_get(&pdev->dev, "vcc"); if (!IS_ERR(s->regulator)) { @@ -938,11 +906,16 @@ static int sccnxp_probe(struct platform_device *pdev) goto err_out; } + if (pdata) + memcpy(&s->pdata, pdata, sizeof(struct sccnxp_pdata)); + if (s->pdata.poll_time_us) { dev_info(&pdev->dev, "Using poll mode, resolution %u usecs\n", s->pdata.poll_time_us); s->poll = 1; - } else { + } + + if (!s->poll) { s->irq = platform_get_irq(pdev, 0); if (s->irq < 0) { dev_err(&pdev->dev, "Missing irq resource data\n"); @@ -1043,9 +1016,8 @@ static int sccnxp_remove(struct platform_device *pdev) static struct platform_driver sccnxp_uart_driver = { .driver = { - .name = SCCNXP_NAME, - .owner = THIS_MODULE, - .of_match_table = sccnxp_dt_id_table, + .name = SCCNXP_NAME, + .owner = THIS_MODULE, }, .probe = sccnxp_probe, .remove = sccnxp_remove, diff --git a/include/linux/platform_data/serial-sccnxp.h b/include/linux/platform_data/serial-sccnxp.h index 98373d6add27..af0c8c3b89ae 100644 --- a/include/linux/platform_data/serial-sccnxp.h +++ b/include/linux/platform_data/serial-sccnxp.h @@ -78,11 +78,11 @@ /* SCCNXP platform data structure */ struct sccnxp_pdata { /* Shift for A0 line */ - u32 reg_shift; + const u8 reg_shift; /* Modem control lines configuration */ - u32 mctrl_cfg[SCCNXP_MAX_UARTS]; + const u32 mctrl_cfg[SCCNXP_MAX_UARTS]; /* Timer value for polling mode (usecs) */ - u32 poll_time_us; + const unsigned int poll_time_us; }; #endif -- cgit v1.2.3-71-gd317 From b6aa23ccaea1548cfb404b41129717f13443bccf Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 1 Aug 2013 16:08:57 -0600 Subject: spi: fix SPI_BIT_MASK so it always fits into 32-bits On a 64-bit platform, ~0UL fills 64-bits, which causes SPI_BIT_MASK(32) not to fit into 32 bits. This causes a warning when the result is assigned to a 32-bit variable. Use ~0U instead to prevent this. This fixes: drivers/spi/spi-gpio.c: In function 'spi_gpio_probe': drivers/spi/spi-gpio.c:446:2: warning: large integer implicitly truncated to unsigned type [-Woverflow] Reported-by: kbuild test robot Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- include/linux/spi/spi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 28e440be1c07..c920c2f4dad5 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -309,7 +309,7 @@ struct spi_master { /* bitmask of supported bits_per_word for transfers */ u32 bits_per_word_mask; #define SPI_BPW_MASK(bits) BIT((bits) - 1) -#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0UL : (BIT(bits) - 1)) +#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1)) #define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1)) /* other constraints relevant to this driver */ -- cgit v1.2.3-71-gd317 From abe4c51afda02423c81da6f1eaaa50b733f1ecc2 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 2 Aug 2013 12:59:44 +0200 Subject: regulators: max8660: add DT bindings This patch adds devicetree bindings for max8660, along with some documentation. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/max8660.txt | 47 +++++++++++++ drivers/regulator/max8660.c | 82 +++++++++++++++++++++- include/linux/regulator/max8660.h | 2 +- 3 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/regulator/max8660.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/regulator/max8660.txt b/Documentation/devicetree/bindings/regulator/max8660.txt new file mode 100644 index 000000000000..8ba994d8a142 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/max8660.txt @@ -0,0 +1,47 @@ +Maxim MAX8660 voltage regulator + +Required properties: +- compatible: must be one of "maxim,max8660", "maxim,max8661" +- reg: I2C slave address, usually 0x34 +- any required generic properties defined in regulator.txt + +Example: + + i2c_master { + max8660@34 { + compatible = "maxim,max8660"; + reg = <0x34>; + + regulators { + regulator@0 { + regulator-compatible= "V3(DCDC)"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1800000>; + }; + + regulator@1 { + regulator-compatible= "V4(DCDC)"; + regulator-min-microvolt = <725000>; + regulator-max-microvolt = <1800000>; + }; + + regulator@2 { + regulator-compatible= "V5(LDO)"; + regulator-min-microvolt = <1700000>; + regulator-max-microvolt = <2000000>; + }; + + regulator@3 { + regulator-compatible= "V6(LDO)"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + regulator@4 { + regulator-compatible= "V7(LDO)"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + }; + }; + }; diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index 74c11aa00c8a..a4f2f1a38342 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -44,6 +44,9 @@ #include #include #include +#include +#include +#include #define MAX8660_DCDC_MIN_UV 725000 #define MAX8660_DCDC_MAX_UV 1800000 @@ -310,6 +313,63 @@ enum { MAX8661 = 1, }; +#ifdef CONFIG_OF +static const struct of_device_id max8660_dt_ids[] = { + { .compatible = "maxim,max8660", .data = (void *) MAX8660 }, + { .compatible = "maxim,max8661", .data = (void *) MAX8661 }, + { } +}; +MODULE_DEVICE_TABLE(of, max8660_dt_ids); + +static int max8660_pdata_from_dt(struct device *dev, + struct device_node **of_node, + struct max8660_platform_data *pdata) +{ + int matched, i; + struct device_node *np; + struct max8660_subdev_data *sub; + struct of_regulator_match rmatch[ARRAY_SIZE(max8660_reg)]; + + np = of_find_node_by_name(dev->of_node, "regulators"); + if (!np) { + dev_err(dev, "missing 'regulators' subnode in DT\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(rmatch); i++) + rmatch[i].name = max8660_reg[i].name; + + matched = of_regulator_match(dev, np, rmatch, ARRAY_SIZE(rmatch)); + if (matched <= 0) + return matched; + + pdata->subdevs = devm_kzalloc(dev, sizeof(struct max8660_subdev_data) * + matched, GFP_KERNEL); + if (!pdata->subdevs) + return -ENOMEM; + + pdata->num_subdevs = matched; + sub = pdata->subdevs; + + for (i = 0; i < matched; i++) { + sub->id = i; + sub->name = rmatch[i].name; + sub->platform_data = rmatch[i].init_data; + of_node[i] = rmatch[i].of_node; + sub++; + } + + return 0; +} +#else +static inline int max8660_pdata_from_dt(struct device *dev, + struct device_node **of_node, + struct max8660_platform_data **pdata) +{ + return 0; +} +#endif + static int max8660_probe(struct i2c_client *client, const struct i2c_device_id *i2c_id) { @@ -319,8 +379,28 @@ static int max8660_probe(struct i2c_client *client, struct regulator_config config = { }; struct max8660 *max8660; int boot_on, i, id, ret = -EINVAL; + struct device_node *of_node[MAX8660_V_END]; unsigned int type; + if (dev->of_node && !pdata) { + const struct of_device_id *id; + struct max8660_platform_data pdata_of; + + id = of_match_device(of_match_ptr(max8660_dt_ids), dev); + if (!id) + return -ENODEV; + + ret = max8660_pdata_from_dt(dev, of_node, &pdata_of); + if (ret < 0) + return ret; + + pdata = &pdata_of; + type = (unsigned int) id->data; + } else { + type = i2c_id->driver_data; + memset(of_node, 0, sizeof(of_node)); + } + if (pdata->num_subdevs > MAX8660_V_END) { dev_err(dev, "Too many regulators found!\n"); return -EINVAL; @@ -334,7 +414,6 @@ static int max8660_probe(struct i2c_client *client, max8660->client = client; rdev = max8660->rdev; - type = i2c_id->driver_data; if (pdata->en34_is_high) { /* Simulate always on */ @@ -407,6 +486,7 @@ static int max8660_probe(struct i2c_client *client, config.dev = dev; config.init_data = pdata->subdevs[i].platform_data; + config.of_node = of_node[i]; config.driver_data = max8660; rdev[i] = regulator_register(&max8660_reg[id], &config); diff --git a/include/linux/regulator/max8660.h b/include/linux/regulator/max8660.h index 9936763621c7..f8a6a4844864 100644 --- a/include/linux/regulator/max8660.h +++ b/include/linux/regulator/max8660.h @@ -39,7 +39,7 @@ enum { */ struct max8660_subdev_data { int id; - char *name; + const char *name; struct regulator_init_data *platform_data; }; -- cgit v1.2.3-71-gd317 From e216975ad97cfcfc436789aa66d59a0e93f337f7 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 1 Aug 2013 16:17:47 -0700 Subject: uapi: Convert some uses of 6 to ETH_ALEN Use the #define where appropriate. Add #include where appropriate too. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/uapi/linux/dn.h | 3 ++- include/uapi/linux/if_bridge.h | 3 ++- include/uapi/linux/netfilter_bridge/ebt_802_3.h | 5 +++-- include/uapi/linux/netfilter_ipv4/ipt_CLUSTERIP.h | 3 ++- include/uapi/linux/virtio_net.h | 2 +- include/uapi/linux/wimax/i2400m.h | 4 ++-- 6 files changed, 12 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/dn.h b/include/uapi/linux/dn.h index 9c50445462d9..5fbdd3d49eba 100644 --- a/include/uapi/linux/dn.h +++ b/include/uapi/linux/dn.h @@ -2,6 +2,7 @@ #define _LINUX_DN_H #include +#include /* @@ -120,7 +121,7 @@ struct linkinfo_dn { * Ethernet address format (for DECnet) */ union etheraddress { - __u8 dne_addr[6]; /* Full ethernet address */ + __u8 dne_addr[ETH_ALEN]; /* Full ethernet address */ struct { __u8 dne_hiord[4]; /* DECnet HIORD prefix */ __u8 dne_nodeaddr[2]; /* DECnet node address */ diff --git a/include/uapi/linux/if_bridge.h b/include/uapi/linux/if_bridge.h index 2d70d79ce2fd..39f621a9fe82 100644 --- a/include/uapi/linux/if_bridge.h +++ b/include/uapi/linux/if_bridge.h @@ -14,6 +14,7 @@ #define _UAPI_LINUX_IF_BRIDGE_H #include +#include #define SYSFS_BRIDGE_ATTR "bridge" #define SYSFS_BRIDGE_FDB "brforward" @@ -88,7 +89,7 @@ struct __port_info { }; struct __fdb_entry { - __u8 mac_addr[6]; + __u8 mac_addr[ETH_ALEN]; __u8 port_no; __u8 is_local; __u32 ageing_timer_value; diff --git a/include/uapi/linux/netfilter_bridge/ebt_802_3.h b/include/uapi/linux/netfilter_bridge/ebt_802_3.h index 5bf84912a082..f37522aade24 100644 --- a/include/uapi/linux/netfilter_bridge/ebt_802_3.h +++ b/include/uapi/linux/netfilter_bridge/ebt_802_3.h @@ -2,6 +2,7 @@ #define _UAPI__LINUX_BRIDGE_EBT_802_3_H #include +#include #define EBT_802_3_SAP 0x01 #define EBT_802_3_TYPE 0x02 @@ -42,8 +43,8 @@ struct hdr_ni { }; struct ebt_802_3_hdr { - __u8 daddr[6]; - __u8 saddr[6]; + __u8 daddr[ETH_ALEN]; + __u8 saddr[ETH_ALEN]; __be16 len; union { struct hdr_ui ui; diff --git a/include/uapi/linux/netfilter_ipv4/ipt_CLUSTERIP.h b/include/uapi/linux/netfilter_ipv4/ipt_CLUSTERIP.h index c6a204c97047..eac0f6548f47 100644 --- a/include/uapi/linux/netfilter_ipv4/ipt_CLUSTERIP.h +++ b/include/uapi/linux/netfilter_ipv4/ipt_CLUSTERIP.h @@ -2,6 +2,7 @@ #define _IPT_CLUSTERIP_H_target #include +#include enum clusterip_hashmode { CLUSTERIP_HASHMODE_SIP = 0, @@ -22,7 +23,7 @@ struct ipt_clusterip_tgt_info { __u32 flags; /* only relevant for new ones */ - __u8 clustermac[6]; + __u8 clustermac[ETH_ALEN]; __u16 num_total_nodes; __u16 num_local_nodes; __u16 local_nodes[CLUSTERIP_MAX_NODES]; diff --git a/include/uapi/linux/virtio_net.h b/include/uapi/linux/virtio_net.h index 227d4ce84e79..172a7f00780c 100644 --- a/include/uapi/linux/virtio_net.h +++ b/include/uapi/linux/virtio_net.h @@ -60,7 +60,7 @@ struct virtio_net_config { /* The config defining mac address (if VIRTIO_NET_F_MAC) */ - __u8 mac[6]; + __u8 mac[ETH_ALEN]; /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ __u16 status; /* Maximum number of each of transmit and receive queues; diff --git a/include/uapi/linux/wimax/i2400m.h b/include/uapi/linux/wimax/i2400m.h index 62d356153565..fd198bc24a3c 100644 --- a/include/uapi/linux/wimax/i2400m.h +++ b/include/uapi/linux/wimax/i2400m.h @@ -122,7 +122,7 @@ #define __LINUX__WIMAX__I2400M_H__ #include - +#include /* * Host Device Interface (HDI) common to all busses @@ -487,7 +487,7 @@ struct i2400m_tlv_l4_message_versions { struct i2400m_tlv_detailed_device_info { struct i2400m_tlv_hdr hdr; __u8 reserved1[400]; - __u8 mac_address[6]; + __u8 mac_address[ETH_ALEN]; __u8 reserved2[2]; } __attribute__((packed)); -- cgit v1.2.3-71-gd317 From 574e2af7c0af3273836def5e66f236521bb433c9 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 1 Aug 2013 16:17:48 -0700 Subject: include: Convert ethernet mac address declarations to use ETH_ALEN It's convenient to have ethernet mac addresses use ETH_ALEN to be able to grep for them a bit easier and also to ensure that the addresses are __aligned(2). Add #include as necessary. Signed-off-by: Joe Perches Acked-by: Mauro Carvalho Chehab Signed-off-by: David S. Miller --- include/linux/dm9000.h | 4 ++- include/linux/fs_enet_pd.h | 3 ++- include/linux/ieee80211.h | 59 +++++++++++++++++++++-------------------- include/linux/mlx4/device.h | 11 ++++---- include/linux/mlx4/qp.h | 5 ++-- include/linux/mv643xx_eth.h | 3 ++- include/linux/sh_eth.h | 3 ++- include/linux/smsc911x.h | 3 ++- include/linux/uwb/spec.h | 5 ++-- include/media/tveeprom.h | 4 ++- include/net/irda/irlan_common.h | 3 ++- 11 files changed, 58 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/include/linux/dm9000.h b/include/linux/dm9000.h index 96e87693d933..841925fbfe8a 100644 --- a/include/linux/dm9000.h +++ b/include/linux/dm9000.h @@ -14,6 +14,8 @@ #ifndef __DM9000_PLATFORM_DATA #define __DM9000_PLATFORM_DATA __FILE__ +#include + /* IO control flags */ #define DM9000_PLATF_8BITONLY (0x0001) @@ -27,7 +29,7 @@ struct dm9000_plat_data { unsigned int flags; - unsigned char dev_addr[6]; + unsigned char dev_addr[ETH_ALEN]; /* allow replacement IO routines */ diff --git a/include/linux/fs_enet_pd.h b/include/linux/fs_enet_pd.h index 51b793466ff3..343d82a54468 100644 --- a/include/linux/fs_enet_pd.h +++ b/include/linux/fs_enet_pd.h @@ -18,6 +18,7 @@ #include #include +#include #include #define FS_ENET_NAME "fs_enet" @@ -135,7 +136,7 @@ struct fs_platform_info { const struct fs_mii_bus_info *bus_info; int rx_ring, tx_ring; /* number of buffers on rx */ - __u8 macaddr[6]; /* mac address */ + __u8 macaddr[ETH_ALEN]; /* mac address */ int rx_copybreak; /* limit we copy small frames */ int use_napi; /* use NAPI */ int napi_weight; /* NAPI weight */ diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b0dc87a2a376..4e101af9c1ff 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -16,6 +16,7 @@ #define LINUX_IEEE80211_H #include +#include #include /* @@ -209,28 +210,28 @@ static inline u16 ieee80211_sn_sub(u16 sn1, u16 sn2) struct ieee80211_hdr { __le16 frame_control; __le16 duration_id; - u8 addr1[6]; - u8 addr2[6]; - u8 addr3[6]; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; __le16 seq_ctrl; - u8 addr4[6]; + u8 addr4[ETH_ALEN]; } __packed __aligned(2); struct ieee80211_hdr_3addr { __le16 frame_control; __le16 duration_id; - u8 addr1[6]; - u8 addr2[6]; - u8 addr3[6]; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; __le16 seq_ctrl; } __packed __aligned(2); struct ieee80211_qos_hdr { __le16 frame_control; __le16 duration_id; - u8 addr1[6]; - u8 addr2[6]; - u8 addr3[6]; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; __le16 seq_ctrl; __le16 qos_ctrl; } __packed __aligned(2); @@ -608,8 +609,8 @@ struct ieee80211s_hdr { u8 flags; u8 ttl; __le32 seqnum; - u8 eaddr1[6]; - u8 eaddr2[6]; + u8 eaddr1[ETH_ALEN]; + u8 eaddr2[ETH_ALEN]; } __packed __aligned(2); /* Mesh flags */ @@ -758,7 +759,7 @@ struct ieee80211_rann_ie { u8 rann_flags; u8 rann_hopcount; u8 rann_ttl; - u8 rann_addr[6]; + u8 rann_addr[ETH_ALEN]; __le32 rann_seq; __le32 rann_interval; __le32 rann_metric; @@ -802,9 +803,9 @@ enum ieee80211_vht_opmode_bits { struct ieee80211_mgmt { __le16 frame_control; __le16 duration; - u8 da[6]; - u8 sa[6]; - u8 bssid[6]; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; + u8 bssid[ETH_ALEN]; __le16 seq_ctrl; union { struct { @@ -833,7 +834,7 @@ struct ieee80211_mgmt { struct { __le16 capab_info; __le16 listen_interval; - u8 current_ap[6]; + u8 current_ap[ETH_ALEN]; /* followed by SSID and Supported rates */ u8 variable[0]; } __packed reassoc_req; @@ -966,21 +967,21 @@ struct ieee80211_vendor_ie { struct ieee80211_rts { __le16 frame_control; __le16 duration; - u8 ra[6]; - u8 ta[6]; + u8 ra[ETH_ALEN]; + u8 ta[ETH_ALEN]; } __packed __aligned(2); struct ieee80211_cts { __le16 frame_control; __le16 duration; - u8 ra[6]; + u8 ra[ETH_ALEN]; } __packed __aligned(2); struct ieee80211_pspoll { __le16 frame_control; __le16 aid; - u8 bssid[6]; - u8 ta[6]; + u8 bssid[ETH_ALEN]; + u8 ta[ETH_ALEN]; } __packed __aligned(2); /* TDLS */ @@ -989,14 +990,14 @@ struct ieee80211_pspoll { struct ieee80211_tdls_lnkie { u8 ie_type; /* Link Identifier IE */ u8 ie_len; - u8 bssid[6]; - u8 init_sta[6]; - u8 resp_sta[6]; + u8 bssid[ETH_ALEN]; + u8 init_sta[ETH_ALEN]; + u8 resp_sta[ETH_ALEN]; } __packed; struct ieee80211_tdls_data { - u8 da[6]; - u8 sa[6]; + u8 da[ETH_ALEN]; + u8 sa[ETH_ALEN]; __be16 ether_type; u8 payload_type; u8 category; @@ -1090,8 +1091,8 @@ struct ieee80211_p2p_noa_attr { struct ieee80211_bar { __le16 frame_control; __le16 duration; - __u8 ra[6]; - __u8 ta[6]; + __u8 ra[ETH_ALEN]; + __u8 ta[ETH_ALEN]; __le16 control; __le16 start_seq_num; } __packed; diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 6aebdfe0ed8b..09ef2f448a00 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -33,6 +33,7 @@ #ifndef MLX4_DEVICE_H #define MLX4_DEVICE_H +#include #include #include #include @@ -620,7 +621,7 @@ struct mlx4_eth_av { u8 dgid[16]; u32 reserved4[2]; __be16 vlan; - u8 mac[6]; + u8 mac[ETH_ALEN]; }; union mlx4_ext_av { @@ -914,10 +915,10 @@ enum mlx4_net_trans_promisc_mode { }; struct mlx4_spec_eth { - u8 dst_mac[6]; - u8 dst_mac_msk[6]; - u8 src_mac[6]; - u8 src_mac_msk[6]; + u8 dst_mac[ETH_ALEN]; + u8 dst_mac_msk[ETH_ALEN]; + u8 src_mac[ETH_ALEN]; + u8 src_mac_msk[ETH_ALEN]; u8 ether_type_enable; __be16 ether_type; __be16 vlan_id_msk; diff --git a/include/linux/mlx4/qp.h b/include/linux/mlx4/qp.h index 262deac02c9e..6d351473c292 100644 --- a/include/linux/mlx4/qp.h +++ b/include/linux/mlx4/qp.h @@ -34,6 +34,7 @@ #define MLX4_QP_H #include +#include #include @@ -143,7 +144,7 @@ struct mlx4_qp_path { u8 feup; u8 fvl_rx; u8 reserved4[2]; - u8 dmac[6]; + u8 dmac[ETH_ALEN]; }; enum { /* fl */ @@ -318,7 +319,7 @@ struct mlx4_wqe_datagram_seg { __be32 dqpn; __be32 qkey; __be16 vlan; - u8 mac[6]; + u8 mac[ETH_ALEN]; }; struct mlx4_wqe_lso_seg { diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index 6e8215b15998..61a0da38d0cb 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -6,6 +6,7 @@ #define __LINUX_MV643XX_ETH_H #include +#include #define MV643XX_ETH_SHARED_NAME "mv643xx_eth" #define MV643XX_ETH_NAME "mv643xx_eth_port" @@ -48,7 +49,7 @@ struct mv643xx_eth_platform_data { * Use this MAC address if it is valid, overriding the * address that is already in the hardware. */ - u8 mac_addr[6]; + u8 mac_addr[ETH_ALEN]; /* * If speed is 0, autonegotiation is enabled. diff --git a/include/linux/sh_eth.h b/include/linux/sh_eth.h index fc305713fc6d..6205eeba392b 100644 --- a/include/linux/sh_eth.h +++ b/include/linux/sh_eth.h @@ -2,6 +2,7 @@ #define __ASM_SH_ETH_H__ #include +#include enum {EDMAC_LITTLE_ENDIAN, EDMAC_BIG_ENDIAN}; enum { @@ -18,7 +19,7 @@ struct sh_eth_plat_data { phy_interface_t phy_interface; void (*set_mdio_gate)(void *addr); - unsigned char mac_addr[6]; + unsigned char mac_addr[ETH_ALEN]; unsigned no_ether_link:1; unsigned ether_link_active_low:1; unsigned needs_init:1; diff --git a/include/linux/smsc911x.h b/include/linux/smsc911x.h index 4dde70e74822..eec3efd19beb 100644 --- a/include/linux/smsc911x.h +++ b/include/linux/smsc911x.h @@ -22,6 +22,7 @@ #define __LINUX_SMSC911X_H__ #include +#include /* platform_device configuration data, should be assigned to * the platform_device's dev.platform_data */ @@ -31,7 +32,7 @@ struct smsc911x_platform_config { unsigned int flags; unsigned int shift; phy_interface_t phy_interface; - unsigned char mac[6]; + unsigned char mac[ETH_ALEN]; }; /* Constants for platform_device irq polarity configuration */ diff --git a/include/linux/uwb/spec.h b/include/linux/uwb/spec.h index b52e44f1bd33..0df24bfcdb38 100644 --- a/include/linux/uwb/spec.h +++ b/include/linux/uwb/spec.h @@ -32,6 +32,7 @@ #include #include +#include #define i1480_FW 0x00000303 /* #define i1480_FW 0x00000302 */ @@ -130,7 +131,7 @@ enum { UWB_DRP_BACKOFF_WIN_MAX = 16 }; * it is also used to define headers sent down and up the wire/radio). */ struct uwb_mac_addr { - u8 data[6]; + u8 data[ETH_ALEN]; } __attribute__((packed)); @@ -568,7 +569,7 @@ struct uwb_rc_evt_confirm { /* Device Address Management event. [WHCI] section 3.1.3.2. */ struct uwb_rc_evt_dev_addr_mgmt { struct uwb_rceb rceb; - u8 baAddr[6]; + u8 baAddr[ETH_ALEN]; u8 bResultCode; } __attribute__((packed)); diff --git a/include/media/tveeprom.h b/include/media/tveeprom.h index 4a1191abd936..f7119ee3977b 100644 --- a/include/media/tveeprom.h +++ b/include/media/tveeprom.h @@ -12,6 +12,8 @@ enum tveeprom_audio_processor { TVEEPROM_AUDPROC_OTHER, }; +#include + struct tveeprom { u32 has_radio; /* If has_ir == 0, then it is unknown what the IR capabilities are, @@ -40,7 +42,7 @@ struct tveeprom { u32 revision; u32 serial_number; char rev_str[5]; - u8 MAC_address[6]; + u8 MAC_address[ETH_ALEN]; }; void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, diff --git a/include/net/irda/irlan_common.h b/include/net/irda/irlan_common.h index 0af8b8dfbc22..550c2d6ec7ff 100644 --- a/include/net/irda/irlan_common.h +++ b/include/net/irda/irlan_common.h @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -161,7 +162,7 @@ struct irlan_provider_cb { int access_type; /* Access type */ __u16 send_arb_val; - __u8 mac_address[6]; /* Generated MAC address for peer device */ + __u8 mac_address[ETH_ALEN]; /* Generated MAC address for peer device */ }; /* -- cgit v1.2.3-71-gd317 From d27fc78208b53ccdfd6a57d4ac44a459ca66806f Mon Sep 17 00:00:00 2001 From: "fan.du" Date: Fri, 2 Aug 2013 10:45:13 +0800 Subject: sctp: Don't lookup dst if transport dst is still valid When sctp sits on IPv6, sctp_transport_dst_check pass cookie as ZERO, as a result ip6_dst_check always fail out. This behaviour makes transport->dst useless, because every sctp_packet_transmit must look for valid dst. Add a dst_cookie into sctp_transport, and set the cookie whenever we get new dst for sctp_transport. So dst validness could be checked against it. Since I have split genid for IPv4 and IPv6, also delete/add IPv6 address will also bump IPv6 genid. So issues we discussed in: http://marc.info/?l=linux-netdev&m=137404469219410&w=4 have all been sloved for this patch. Signed-off-by: Fan Du Acked-by: Vlad Yasevich Acked-by: Neil Horman Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 2 +- include/net/sctp/structs.h | 1 + net/sctp/ipv6.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 554cf88605fc..cb28df9c2781 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -613,7 +613,7 @@ static inline void sctp_v4_map_v6(union sctp_addr *addr) */ static inline struct dst_entry *sctp_transport_dst_check(struct sctp_transport *t) { - if (t->dst && !dst_check(t->dst, 0)) { + if (t->dst && !dst_check(t->dst, t->dst_cookie)) { dst_release(t->dst); t->dst = NULL; } diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 75c4c16601b6..c0f4e29eedd5 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -946,6 +946,7 @@ struct sctp_transport { __u64 hb_nonce; struct rcu_head rcu; + u32 dst_cookie; }; struct sctp_transport *sctp_transport_new(struct net *, const union sctp_addr *, diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 85d688f59e6a..5a9402e3c0cf 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -351,7 +351,7 @@ out: rt = (struct rt6_info *)dst; t->dst = dst; - + t->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; pr_debug("rt6_dst:%pI6 rt6_src:%pI6\n", &rt->rt6i_dst.addr, &fl6->saddr); } else { -- cgit v1.2.3-71-gd317 From 6ef94cfafba159d6b1a902ccb3349ac6a34ff6ad Mon Sep 17 00:00:00 2001 From: Stefan Tomanek Date: Fri, 2 Aug 2013 17:19:56 +0200 Subject: fib_rules: add route suppression based on ifgroup This change adds the ability to suppress a routing decision based upon the interface group the selected interface belongs to. This allows it to exclude specific devices from a routing decision. Signed-off-by: Stefan Tomanek Signed-off-by: David S. Miller --- include/net/fib_rules.h | 2 ++ include/uapi/linux/fib_rules.h | 2 +- net/core/fib_rules.c | 10 ++++++++++ net/ipv4/fib_rules.c | 23 +++++++++++++++++------ net/ipv6/fib6_rules.c | 16 +++++++++++++--- 5 files changed, 43 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 2f286dce9259..d13c461b4b59 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -18,6 +18,7 @@ struct fib_rule { u32 pref; u32 flags; u32 table; + int suppress_ifgroup; u8 table_prefixlen_min; u8 action; u32 target; @@ -84,6 +85,7 @@ struct fib_rules_ops { [FRA_FWMASK] = { .type = NLA_U32 }, \ [FRA_TABLE] = { .type = NLA_U32 }, \ [FRA_TABLE_PREFIXLEN_MIN] = { .type = NLA_U8 }, \ + [FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32 }, \ [FRA_GOTO] = { .type = NLA_U32 } static inline void fib_rule_get(struct fib_rule *rule) diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h index 59cd31b3455e..63e31166e85b 100644 --- a/include/uapi/linux/fib_rules.h +++ b/include/uapi/linux/fib_rules.h @@ -44,7 +44,7 @@ enum { FRA_FWMARK, /* mark */ FRA_FLOW, /* flow/class id */ FRA_UNUSED6, - FRA_UNUSED7, + FRA_SUPPRESS_IFGROUP, FRA_TABLE_PREFIXLEN_MIN, FRA_TABLE, /* Extended table id */ FRA_FWMASK, /* mask for netfilter mark */ diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 2ef5040c99c8..5040a61bf28a 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -343,6 +343,9 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh) if (tb[FRA_TABLE_PREFIXLEN_MIN]) rule->table_prefixlen_min = nla_get_u8(tb[FRA_TABLE_PREFIXLEN_MIN]); + if (tb[FRA_SUPPRESS_IFGROUP]) + rule->suppress_ifgroup = nla_get_u32(tb[FRA_SUPPRESS_IFGROUP]); + if (!tb[FRA_PRIORITY] && ops->default_pref) rule->pref = ops->default_pref(ops); @@ -529,6 +532,7 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + nla_total_size(4) /* FRA_PRIORITY */ + nla_total_size(4) /* FRA_TABLE */ + nla_total_size(1) /* FRA_TABLE_PREFIXLEN_MIN */ + + nla_total_size(4) /* FRA_SUPPRESS_IFGROUP */ + nla_total_size(4) /* FRA_FWMARK */ + nla_total_size(4); /* FRA_FWMASK */ @@ -588,6 +592,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, (rule->target && nla_put_u32(skb, FRA_GOTO, rule->target))) goto nla_put_failure; + + if (rule->suppress_ifgroup != -1) { + if (nla_put_u32(skb, FRA_SUPPRESS_IFGROUP, rule->suppress_ifgroup)) + goto nla_put_failure; + } + if (ops->fill(rule, skb, frh) < 0) goto nla_put_failure; diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 9f2906679d1f..b78fd28970c9 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -103,16 +103,27 @@ errout: static bool fib4_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg *arg) { + struct fib_result *result = (struct fib_result *) arg->result; + struct net_device *dev = result->fi->fib_dev; + /* do not accept result if the route does * not meet the required prefix length */ - struct fib_result *result = (struct fib_result *) arg->result; - if (result->prefixlen < rule->table_prefixlen_min) { - if (!(arg->flags & FIB_LOOKUP_NOREF)) - fib_info_put(result->fi); - return true; - } + if (result->prefixlen < rule->table_prefixlen_min) + goto suppress_route; + + /* do not accept result if the route uses a device + * belonging to a forbidden interface group + */ + if (rule->suppress_ifgroup != -1 && dev && dev->group == rule->suppress_ifgroup) + goto suppress_route; + return false; + +suppress_route: + if (!(arg->flags & FIB_LOOKUP_NOREF)) + fib_info_put(result->fi); + return true; } static int fib4_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index 554a4fbabfb3..36283267e2f8 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -122,14 +122,24 @@ out: static bool fib6_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg *arg) { struct rt6_info *rt = (struct rt6_info *) arg->result; + struct net_device *dev = rt->rt6i_idev->dev; /* do not accept result if the route does * not meet the required prefix length */ - if (rt->rt6i_dst.plen < rule->table_prefixlen_min) { + if (rt->rt6i_dst.plen < rule->table_prefixlen_min) + goto suppress_route; + + /* do not accept result if the route uses a device + * belonging to a forbidden interface group + */ + if (rule->suppress_ifgroup != -1 && dev && dev->group == rule->suppress_ifgroup) + goto suppress_route; + + return false; + +suppress_route: ip6_rt_put(rt); return true; - } - return false; } static int fib6_rule_match(struct fib_rule *rule, struct flowi *fl, int flags) -- cgit v1.2.3-71-gd317 From 5ef76da644bf346d29200007d8d3779e7009dabb Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Fri, 2 Aug 2013 14:05:20 +0200 Subject: fbdev: simplefb: add init through platform_data If we create proper platform-devices in x86 boot-code, we can use simplefb for VBE or EFI framebuffers, too. However, there is normally no OF support so we introduce a platform_data object so x86 boot-code can pass the parameters via plain old platform-data. This also removes the OF dependency as it is not needed. The headers provide proper dummies for the case OF is disabled. Furthermore, we move the FORMAT-definitions to the common platform header so initialization code can use it to transform "struct screen_info" to the right format-name. Signed-off-by: David Herrmann Link: http://lkml.kernel.org/r/1375445127-15480-2-git-send-email-dh.herrmann@gmail.com Reviewed-by: Stephen Warren Signed-off-by: H. Peter Anvin --- drivers/video/Kconfig | 5 ++- drivers/video/simplefb.c | 48 +++++++++++++++++++++-------- include/linux/platform_data/simplefb.h | 56 ++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 16 deletions(-) create mode 100644 include/linux/platform_data/simplefb.h (limited to 'include') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 4cf1e1dd5621..34c3d960634d 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2457,7 +2457,7 @@ config FB_HYPERV config FB_SIMPLE bool "Simple framebuffer support" - depends on (FB = y) && OF + depends on (FB = y) select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -2469,8 +2469,7 @@ config FB_SIMPLE pre-allocated frame buffer surface. Configuration re: surface address, size, and format must be provided - through device tree, or potentially plain old platform data in the - future. + through device tree, or plain old platform data. source "drivers/video/omap/Kconfig" source "drivers/video/omap2/Kconfig" diff --git a/drivers/video/simplefb.c b/drivers/video/simplefb.c index e2e9e3e61b72..588698986ce1 100644 --- a/drivers/video/simplefb.c +++ b/drivers/video/simplefb.c @@ -24,6 +24,7 @@ #include #include #include +#include #include static struct fb_fix_screeninfo simplefb_fix = { @@ -73,18 +74,7 @@ static struct fb_ops simplefb_ops = { .fb_imageblit = cfb_imageblit, }; -struct simplefb_format { - const char *name; - u32 bits_per_pixel; - struct fb_bitfield red; - struct fb_bitfield green; - struct fb_bitfield blue; - struct fb_bitfield transp; -}; - -static struct simplefb_format simplefb_formats[] = { - { "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0} }, -}; +static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS; struct simplefb_params { u32 width; @@ -139,6 +129,33 @@ static int simplefb_parse_dt(struct platform_device *pdev, return 0; } +static int simplefb_parse_pd(struct platform_device *pdev, + struct simplefb_params *params) +{ + struct simplefb_platform_data *pd = pdev->dev.platform_data; + int i; + + params->width = pd->width; + params->height = pd->height; + params->stride = pd->stride; + + params->format = NULL; + for (i = 0; i < ARRAY_SIZE(simplefb_formats); i++) { + if (strcmp(pd->format, simplefb_formats[i].name)) + continue; + + params->format = &simplefb_formats[i]; + break; + } + + if (!params->format) { + dev_err(&pdev->dev, "Invalid format value\n"); + return -EINVAL; + } + + return 0; +} + static int simplefb_probe(struct platform_device *pdev) { int ret; @@ -149,7 +166,12 @@ static int simplefb_probe(struct platform_device *pdev) if (fb_get_options("simplefb", NULL)) return -ENODEV; - ret = simplefb_parse_dt(pdev, ¶ms); + ret = -ENODEV; + if (pdev->dev.platform_data) + ret = simplefb_parse_pd(pdev, ¶ms); + else if (pdev->dev.of_node) + ret = simplefb_parse_dt(pdev, ¶ms); + if (ret) return ret; diff --git a/include/linux/platform_data/simplefb.h b/include/linux/platform_data/simplefb.h new file mode 100644 index 000000000000..5fa2c5e02ab8 --- /dev/null +++ b/include/linux/platform_data/simplefb.h @@ -0,0 +1,56 @@ +/* + * simplefb.h - Simple Framebuffer Device + * + * Copyright (C) 2013 David Herrmann + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __PLATFORM_DATA_SIMPLEFB_H__ +#define __PLATFORM_DATA_SIMPLEFB_H__ + +#include +#include +#include + +/* format array, use it to initialize a "struct simplefb_format" array */ +#define SIMPLEFB_FORMATS \ +{ \ + { "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0}, DRM_FORMAT_RGB565 }, \ +} + +/* + * Data-Format for Simple-Framebuffers + * @name: unique 0-terminated name that can be used to identify the mode + * @red,green,blue: Offsets and sizes of the single RGB parts + * @transp: Offset and size of the alpha bits. length=0 means no alpha + * @fourcc: 32bit DRM four-CC code (see drm_fourcc.h) + */ +struct simplefb_format { + const char *name; + u32 bits_per_pixel; + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; + u32 fourcc; +}; + +/* + * Simple-Framebuffer description + * If the arch-boot code creates simple-framebuffers without DT support, it + * can pass the width, height, stride and format via this platform-data object. + * The framebuffer location must be given as IORESOURCE_MEM resource. + * @format must be a format as described in "struct simplefb_format" above. + */ +struct simplefb_platform_data { + u32 width; + u32 height; + u32 stride; + const char *format; +}; + +#endif /* __PLATFORM_DATA_SIMPLEFB_H__ */ -- cgit v1.2.3-71-gd317 From 9a6a36d19c9d24479300511e23933876a4a9cf82 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Fri, 2 Aug 2013 14:05:24 +0200 Subject: fbdev: simplefb: add common x86 RGB formats 32bit XRGB and ARGB are used by modern x86 systems for EFI and VESA framebuffers. The other formats were reported by hpa to be most common. Add these so simplefb works on most common x86 systems. Signed-off-by: David Herrmann Link: http://lkml.kernel.org/r/1375445127-15480-6-git-send-email-dh.herrmann@gmail.com Signed-off-by: H. Peter Anvin --- include/linux/platform_data/simplefb.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/platform_data/simplefb.h b/include/linux/platform_data/simplefb.h index 5fa2c5e02ab8..53774b0cd8e9 100644 --- a/include/linux/platform_data/simplefb.h +++ b/include/linux/platform_data/simplefb.h @@ -20,6 +20,13 @@ #define SIMPLEFB_FORMATS \ { \ { "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0}, DRM_FORMAT_RGB565 }, \ + { "x1r5g5b5", 16, {10, 5}, {5, 5}, {0, 5}, {0, 0}, DRM_FORMAT_XRGB1555 }, \ + { "a1r5g5b5", 16, {10, 5}, {5, 5}, {0, 5}, {15, 1}, DRM_FORMAT_ARGB1555 }, \ + { "r8g8b8", 24, {16, 8}, {8, 8}, {0, 8}, {0, 0}, DRM_FORMAT_RGB888 }, \ + { "x8r8g8b8", 32, {16, 8}, {8, 8}, {0, 8}, {0, 0}, DRM_FORMAT_XRGB8888 }, \ + { "a8r8g8b8", 32, {16, 8}, {8, 8}, {0, 8}, {24, 8}, DRM_FORMAT_ARGB8888 }, \ + { "x2r10g10b10", 32, {20, 10}, {10, 10}, {0, 10}, {0, 0}, DRM_FORMAT_XRGB2101010 }, \ + { "a2r10g10b10", 32, {20, 10}, {10, 10}, {0, 10}, {30, 2}, DRM_FORMAT_ARGB2101010 }, \ } /* -- cgit v1.2.3-71-gd317 From 626f090c5cbbe557379978c7a9525011ad7fbbf6 Mon Sep 17 00:00:00 2001 From: Yacine Belkadi Date: Fri, 2 Aug 2013 20:10:04 +0200 Subject: usb: fix some scripts/kernel-doc warnings When building the htmldocs (in verbose mode), scripts/kernel-doc reports the following type of warnings: Warning(drivers/usb/core/usb.c:76): No description found for return value of 'usb_find_alt_setting' Fix them by: - adding some missing descriptions of return values - using "Return" sections for those descriptions Signed-off-by: Yacine Belkadi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/buffer.c | 5 ++-- drivers/usb/core/driver.c | 22 +++++++++++++++-- drivers/usb/core/file.c | 2 +- drivers/usb/core/hcd-pci.c | 2 ++ drivers/usb/core/hcd.c | 37 ++++++++++++++++++++-------- drivers/usb/core/hub.c | 37 +++++++++++++++++++--------- drivers/usb/core/message.c | 49 ++++++++++++++++++++----------------- drivers/usb/core/urb.c | 34 ++++++++++++++++---------- drivers/usb/core/usb.c | 60 ++++++++++++++++++++++++++++------------------ include/linux/usb.h | 12 ++++++---- 10 files changed, 173 insertions(+), 87 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index b0585e623ba9..23559746be92 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -43,10 +43,11 @@ static const size_t pool_max[HCD_BUFFER_POOLS] = { * * Call this as part of initializing a host controller that uses the dma * memory allocators. It initializes some pools of dma-coherent memory that - * will be shared by all drivers using that controller, or returns a negative - * errno value on error. + * will be shared by all drivers using that controller. * * Call hcd_buffer_destroy() to clean up after using those pools. + * + * Return: 0 if successful. A negative errno value otherwise. */ int hcd_buffer_create(struct usb_hcd *hcd) { diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 7609ac4aed1c..124bcc50b0b7 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -117,6 +117,8 @@ static DRIVER_ATTR(new_id, S_IRUGO | S_IWUSR, show_dynids, store_new_id); * @count: input size * * Removes a dynamic usb device ID from this driver. + * + * Return: @count on success. A negative error code otherwise. */ static ssize_t store_remove_id(struct device_driver *driver, const char *buf, size_t count) @@ -457,6 +459,8 @@ static int usb_unbind_interface(struct device *dev) * Callers must own the device lock, so driver probe() entries don't need * extra locking, but other call contexts may need to explicitly claim that * lock. + * + * Return: 0 on success. */ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void *priv) @@ -658,6 +662,8 @@ EXPORT_SYMBOL_GPL(usb_match_one_id); * These device tables are exported with MODULE_DEVICE_TABLE, through * modutils, to support the driver loading functionality of USB hotplugging. * + * Return: The first matching usb_device_id, or %NULL. + * * What Matches: * * The "match_flags" element in a usb_device_id controls which @@ -823,7 +829,8 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env) * Registers a USB device driver with the USB core. The list of * unattached devices will be rescanned whenever a new driver is * added, allowing the new driver to attach to any recognized devices. - * Returns a negative error code on failure and 0 on success. + * + * Return: A negative error code on failure and 0 on success. */ int usb_register_device_driver(struct usb_device_driver *new_udriver, struct module *owner) @@ -879,7 +886,8 @@ EXPORT_SYMBOL_GPL(usb_deregister_device_driver); * Registers a USB interface driver with the USB core. The list of * unattached interfaces will be rescanned whenever a new driver is * added, allowing the new driver to attach to any recognized interfaces. - * Returns a negative error code on failure and 0 on success. + * + * Return: A negative error code on failure and 0 on success. * * NOTE: if you want your driver to use the USB major number, you must call * usb_register_dev() to enable that functionality. This function no longer @@ -1213,6 +1221,8 @@ done: * unpredictable times. * * This routine can run only in process context. + * + * Return: 0 if the suspend succeeded. */ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) { @@ -1294,6 +1304,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) * unpredictable times. * * This routine can run only in process context. + * + * Return: 0 on success. */ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) { @@ -1491,6 +1503,8 @@ void usb_autosuspend_device(struct usb_device *udev) * The caller must hold @udev's device lock. * * This routine can run only in process context. + * + * Return: 0 on success. A negative error code otherwise. */ int usb_autoresume_device(struct usb_device *udev) { @@ -1600,6 +1614,8 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface_no_suspend); * However if the autoresume fails then the counter is re-decremented. * * This routine can run only in process context. + * + * Return: 0 on success. */ int usb_autopm_get_interface(struct usb_interface *intf) { @@ -1633,6 +1649,8 @@ EXPORT_SYMBOL_GPL(usb_autopm_get_interface); * resumed. * * This routine can run in atomic context. + * + * Return: 0 on success. A negative error code otherwise. */ int usb_autopm_get_interface_async(struct usb_interface *intf) { diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 6a4c40766f0f..7421888087a3 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -153,7 +153,7 @@ void usb_major_cleanup(void) * usb_deregister_dev() must be called when the driver is done with * the minor numbers given out by this function. * - * Returns -EINVAL if something bad happens with trying to register a + * Return: -EINVAL if something bad happens with trying to register a * device, and 0 on success. */ int usb_register_dev(struct usb_interface *intf, diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index caeb8d6d39fb..b9d3c43e3859 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -171,6 +171,8 @@ static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd, * through the hotplug entry's driver_data. * * Store this function in the HCD's struct pci_driver as probe(). + * + * Return: 0 if successful. */ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 014dc996b4f6..dc1346fee76c 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -378,9 +378,10 @@ MODULE_PARM_DESC(authorized_default, * @buf: Buffer for USB string descriptor (header + UTF-16LE) * @len: Length (in bytes; may be odd) of descriptor buffer. * - * The return value is the number of bytes filled in: 2 + 2*strlen(s) or - * buflen, whichever is less. + * Return: The number of bytes filled in: 2 + 2*strlen(s) or @len, + * whichever is less. * + * Note: * USB String descriptors can contain at most 126 characters; input * strings longer than that are truncated. */ @@ -416,7 +417,8 @@ ascii2desc(char const *s, u8 *buf, unsigned len) * * Produces either a manufacturer, product or serial number string for the * virtual root hub device. - * Returns the number of bytes filled in: the length of the descriptor or + * + * Return: The number of bytes filled in: the length of the descriptor or * of the provided buffer, whichever is less. */ static unsigned @@ -938,6 +940,8 @@ static void usb_bus_init (struct usb_bus *bus) * * Assigns a bus number, and links the controller into usbcore data * structures so that it can be seen by scanning the bus list. + * + * Return: 0 if successful. A negative error code otherwise. */ static int usb_register_bus(struct usb_bus *bus) { @@ -1002,6 +1006,8 @@ static void usb_deregister_bus (struct usb_bus *bus) * the device properly in the device tree and then calls usb_new_device() * to register the usb device. It also assigns the root hub's USB address * (always 1). + * + * Return: 0 if successful. A negative error code otherwise. */ static int register_root_hub(struct usb_hcd *hcd) { @@ -1108,7 +1114,9 @@ EXPORT_SYMBOL_GPL(usb_hcd_end_port_resume); * @isoc: true for isochronous transactions, false for interrupt ones * @bytecount: how many bytes in the transaction. * - * Returns approximate bus time in nanoseconds for a periodic transaction. + * Return: Approximate bus time in nanoseconds for a periodic transaction. + * + * Note: * See USB 2.0 spec section 5.11.3; only periodic transfers need to be * scheduled in software, this function is only used for such scheduling. */ @@ -1166,7 +1174,7 @@ EXPORT_SYMBOL_GPL(usb_calc_bus_time); * be disabled. The actions carried out here are required for URB * submission, as well as for endpoint shutdown and for usb_kill_urb. * - * Returns 0 for no error, otherwise a negative error code (in which case + * Return: 0 for no error, otherwise a negative error code (in which case * the enqueue() method must fail). If no error occurs but enqueue() fails * anyway, it must call usb_hcd_unlink_urb_from_ep() before releasing * the private spinlock and returning. @@ -1221,7 +1229,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_link_urb_to_ep); * be disabled. The actions carried out here are required for making * sure than an unlink is valid. * - * Returns 0 for no error, otherwise a negative error code (in which case + * Return: 0 for no error, otherwise a negative error code (in which case * the dequeue() method must fail). The possible error codes are: * * -EIDRM: @urb was not submitted or has already completed. @@ -1784,7 +1792,7 @@ rescan: * pass in the current alternate interface setting in cur_alt, * and pass in the new alternate interface setting in new_alt. * - * Returns an error if the requested bandwidth change exceeds the + * Return: An error if the requested bandwidth change exceeds the * bus bandwidth or host controller internal resources. */ int usb_hcd_alloc_bandwidth(struct usb_device *udev, @@ -1954,9 +1962,12 @@ void usb_hcd_reset_endpoint(struct usb_device *udev, * @num_streams: number of streams to allocate. * @mem_flags: flags hcd should use to allocate memory. * - * Sets up a group of bulk endpoints to have num_streams stream IDs available. + * Sets up a group of bulk endpoints to have @num_streams stream IDs available. * Drivers may queue multiple transfers to different stream IDs, which may * complete in a different order than they were queued. + * + * Return: On success, the number of allocated streams. On failure, a negative + * error code. */ int usb_alloc_streams(struct usb_interface *interface, struct usb_host_endpoint **eps, unsigned int num_eps, @@ -2201,6 +2212,8 @@ EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); * khubd identifying and possibly configuring the device. * This is needed by OTG controller drivers, where it helps meet * HNP protocol timing requirements for starting a port reset. + * + * Return: 0 if successful. */ int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num) { @@ -2235,6 +2248,8 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum); * * If the controller isn't HALTed, calls the driver's irq handler. * Checks whether the controller is now dead. + * + * Return: %IRQ_HANDLED if the IRQ was handled. %IRQ_NONE otherwise. */ irqreturn_t usb_hcd_irq (int irq, void *__hcd) { @@ -2320,7 +2335,8 @@ EXPORT_SYMBOL_GPL (usb_hc_died); * HC driver's private data. Initialize the generic members of the * hcd structure. * - * If memory is unavailable, returns NULL. + * Return: On success, a pointer to the created and initialized HCD structure. + * On failure (e.g. if memory is unavailable), %NULL. */ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name, @@ -2384,7 +2400,8 @@ EXPORT_SYMBOL_GPL(usb_create_shared_hcd); * HC driver's private data. Initialize the generic members of the * hcd structure. * - * If memory is unavailable, returns NULL. + * Return: On success, a pointer to the created and initialized HCD + * structure. On failure (e.g. if memory is unavailable), %NULL. */ struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 0416f835cfee..5d6d28a76e0a 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -734,6 +734,8 @@ static void hub_tt_work(struct work_struct *work) * * call this function to control port's power via setting or * clearing the port's PORT_POWER feature. + * + * Return: 0 if successful. A negative error code otherwise. */ int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub, int port1, bool set) @@ -762,6 +764,8 @@ int usb_hub_set_port_power(struct usb_device *hdev, struct usb_hub *hub, * * It may not be possible for that hub to handle additional full (or low) * speed transactions until that state is fully cleared out. + * + * Return: 0 if successful. A negative error code otherwise. */ int usb_hub_clear_tt_buffer(struct urb *urb) { @@ -964,6 +968,8 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) * see that the device has been disconnected. When the device is * physically unplugged and something is plugged in, the events will * be received and processed normally. + * + * Return: 0 if successful. A negative error code otherwise. */ int usb_remove_device(struct usb_device *udev) { @@ -2115,6 +2121,8 @@ static inline void announce_device(struct usb_device *udev) { } * @udev: newly addressed device (in ADDRESS state) * * Finish enumeration for On-The-Go devices + * + * Return: 0 if successful. A negative error code otherwise. */ static int usb_enumerate_device_otg(struct usb_device *udev) { @@ -2197,6 +2205,8 @@ fail: * If the device is WUSB and not authorized, we don't attempt to read * the string descriptors, as they will be errored out by the device * until it has been authorized. + * + * Return: 0 if successful. A negative error code otherwise. */ static int usb_enumerate_device(struct usb_device *udev) { @@ -2277,13 +2287,14 @@ static void set_usb_port_removable(struct usb_device *udev) * udev has already been installed, but udev is not yet visible through * sysfs or other filesystem code. * - * It will return if the device is configured properly or not. Zero if - * the interface was registered with the driver core; else a negative - * errno value. - * * This call is synchronous, and may not be used in an interrupt context. * * Only the hub driver or root-hub registrar should ever call this. + * + * Return: Whether the device is configured properly or not. Zero if the + * interface was registered with the driver core; else a negative errno + * value. + * */ int usb_new_device(struct usb_device *udev) { @@ -2391,6 +2402,8 @@ fail: * * We share a lock (that we have) with device_del(), so we need to * defer its call. + * + * Return: 0. */ int usb_deauthorize_device(struct usb_device *usb_dev) { @@ -5025,10 +5038,11 @@ static int descriptors_changed(struct usb_device *udev, * re-connected. All drivers will be unbound, and the device will be * re-enumerated and probed all over again. * - * Returns 0 if the reset succeeded, -ENODEV if the device has been + * Return: 0 if the reset succeeded, -ENODEV if the device has been * flagged for logical disconnection, or some other negative error code * if the reset wasn't even attempted. * + * Note: * The caller must own the device lock. For example, it's safe to use * this from a driver probe() routine after downloading new firmware. * For calls that might not occur during probe(), drivers should lock @@ -5184,8 +5198,9 @@ re_enumerate: * method), performs the port reset, and then lets the drivers know that * the reset is over (using their post_reset method). * - * Return value is the same as for usb_reset_and_verify_device(). + * Return: The same as for usb_reset_and_verify_device(). * + * Note: * The caller must own the device lock. For example, it's safe to use * this from a driver probe() routine after downloading new firmware. * For calls that might not occur during probe(), drivers should lock @@ -5323,7 +5338,7 @@ EXPORT_SYMBOL_GPL(usb_queue_reset_device); * USB drivers call this function to get hub's child device * pointer. * - * Return NULL if input param is invalid and + * Return: %NULL if input param is invalid and * child's usb_device pointer if non-NULL. */ struct usb_device *usb_hub_find_child(struct usb_device *hdev, @@ -5357,8 +5372,8 @@ void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1, * @hdev: USB device belonging to the usb hub * @port1: port num of the port * - * Return connect type of the port and if input params are - * invalid, return USB_PORT_CONNECT_TYPE_UNKNOWN. + * Return: The connect type of the port if successful. Or + * USB_PORT_CONNECT_TYPE_UNKNOWN if input params are invalid. */ enum usb_port_connect_type usb_get_hub_port_connect_type(struct usb_device *hdev, int port1) @@ -5418,8 +5433,8 @@ void usb_hub_adjust_deviceremovable(struct usb_device *hdev, * @hdev: USB device belonging to the usb hub * @port1: port num of the port * - * Return port's acpi handle if successful, NULL if params are - * invaild. + * Return: Port's acpi handle if successful, %NULL if params are + * invalid. */ acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev, int port1) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 6549a975b0c5..82927e1ed27d 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -119,15 +119,15 @@ static int usb_internal_control_msg(struct usb_device *usb_dev, * This function sends a simple control message to a specified endpoint and * waits for the message to complete, or timeout. * - * If successful, it returns the number of bytes transferred, otherwise a - * negative error number. - * * Don't use this function from within an interrupt context, like a bottom half * handler. If you need an asynchronous message, or need to send a message * from within interrupt context, use usb_submit_urb(). * If a thread in your driver uses this call, make sure your disconnect() * method can wait for it to complete. Since you don't have a handle on the * URB used, you can't cancel the request. + * + * Return: If successful, the number of bytes transferred. Otherwise, a negative + * error number. */ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, @@ -170,15 +170,16 @@ EXPORT_SYMBOL_GPL(usb_control_msg); * This function sends a simple interrupt message to a specified endpoint and * waits for the message to complete, or timeout. * - * If successful, it returns 0, otherwise a negative error number. The number - * of actual bytes transferred will be stored in the actual_length paramater. - * * Don't use this function from within an interrupt context, like a bottom half * handler. If you need an asynchronous message, or need to send a message * from within interrupt context, use usb_submit_urb() If a thread in your * driver uses this call, make sure your disconnect() method can wait for it to * complete. Since you don't have a handle on the URB used, you can't cancel * the request. + * + * Return: + * If successful, 0. Otherwise a negative error number. The number of actual + * bytes transferred will be stored in the @actual_length paramater. */ int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) @@ -203,9 +204,6 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg); * This function sends a simple bulk message to a specified endpoint * and waits for the message to complete, or timeout. * - * If successful, it returns 0, otherwise a negative error number. The number - * of actual bytes transferred will be stored in the actual_length paramater. - * * Don't use this function from within an interrupt context, like a bottom half * handler. If you need an asynchronous message, or need to send a message * from within interrupt context, use usb_submit_urb() If a thread in your @@ -217,6 +215,11 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg); * users are forced to abuse this routine by using it to submit URBs for * interrupt endpoints. We will take the liberty of creating an interrupt URB * (with the default interval) if the target is an interrupt endpoint. + * + * Return: + * If successful, 0. Otherwise a negative error number. The number of actual + * bytes transferred will be stored in the @actual_length paramater. + * */ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout) @@ -341,9 +344,9 @@ static void sg_complete(struct urb *urb) * send every byte identified in the list. * @mem_flags: SLAB_* flags affecting memory allocations in this call * - * Returns zero for success, else a negative errno value. This initializes a - * scatter/gather request, allocating resources such as I/O mappings and urb - * memory (except maybe memory used by USB controller drivers). + * This initializes a scatter/gather request, allocating resources such as + * I/O mappings and urb memory (except maybe memory used by USB controller + * drivers). * * The request must be issued using usb_sg_wait(), which waits for the I/O to * complete (or to be canceled) and then cleans up all resources allocated by @@ -351,6 +354,8 @@ static void sg_complete(struct urb *urb) * * The request may be canceled with usb_sg_cancel(), either before or after * usb_sg_wait() is called. + * + * Return: Zero for success, else a negative errno value. */ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, unsigned pipe, unsigned period, struct scatterlist *sg, @@ -623,7 +628,7 @@ EXPORT_SYMBOL_GPL(usb_sg_cancel); * * This call is synchronous, and may not be used in an interrupt context. * - * Returns the number of bytes received on success, or else the status code + * Return: The number of bytes received on success, or else the status code * returned by the underlying usb_control_msg() call. */ int usb_get_descriptor(struct usb_device *dev, unsigned char type, @@ -671,7 +676,7 @@ EXPORT_SYMBOL_GPL(usb_get_descriptor); * * This call is synchronous, and may not be used in an interrupt context. * - * Returns the number of bytes received on success, or else the status code + * Return: The number of bytes received on success, or else the status code * returned by the underlying usb_control_msg() call. */ static int usb_get_string(struct usb_device *dev, unsigned short langid, @@ -805,7 +810,7 @@ static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) * * This call is synchronous, and may not be used in an interrupt context. * - * Returns length of the string (>= 0) or usb_control_msg status (< 0). + * Return: length of the string (>= 0) or usb_control_msg status (< 0). */ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) { @@ -853,8 +858,8 @@ EXPORT_SYMBOL_GPL(usb_string); * @udev: the device whose string descriptor is being read * @index: the descriptor index * - * Returns a pointer to a kmalloc'ed buffer containing the descriptor string, - * or NULL if the index is 0 or the string could not be read. + * Return: A pointer to a kmalloc'ed buffer containing the descriptor string, + * or %NULL if the index is 0 or the string could not be read. */ char *usb_cache_string(struct usb_device *udev, int index) { @@ -894,7 +899,7 @@ char *usb_cache_string(struct usb_device *udev, int index) * * This call is synchronous, and may not be used in an interrupt context. * - * Returns the number of bytes received on success, or else the status code + * Return: The number of bytes received on success, or else the status code * returned by the underlying usb_control_msg() call. */ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) @@ -980,7 +985,7 @@ EXPORT_SYMBOL_GPL(usb_get_status); * * This call is synchronous, and may not be used in an interrupt context. * - * Returns zero on success, or else the status code returned by the + * Return: Zero on success, or else the status code returned by the * underlying usb_control_msg() call. */ int usb_clear_halt(struct usb_device *dev, int pipe) @@ -1277,7 +1282,7 @@ void usb_enable_interface(struct usb_device *dev, * endpoints in that interface; all such urbs must first be completed * (perhaps forced by unlinking). * - * Returns zero on success, or else the status code returned by the + * Return: Zero on success, or else the status code returned by the * underlying usb_control_msg() call. */ int usb_set_interface(struct usb_device *dev, int interface, int alternate) @@ -1431,7 +1436,7 @@ EXPORT_SYMBOL_GPL(usb_set_interface); * * The caller must own the device lock. * - * Returns zero on success, else a negative error code. + * Return: Zero on success, else a negative error code. */ int usb_reset_configuration(struct usb_device *dev) { @@ -1973,7 +1978,7 @@ static void cancel_async_set_config(struct usb_device *udev) * routine gets around the normal restrictions by using a work thread to * submit the change-config request. * - * Returns 0 if the request was successfully queued, error code otherwise. + * Return: 0 if the request was successfully queued, error code otherwise. * The caller has no way to know whether the queued request will eventually * succeed. */ diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index e75115a04a2e..4e7a311ff6d7 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -55,12 +55,12 @@ EXPORT_SYMBOL_GPL(usb_init_urb); * Creates an urb for the USB driver to use, initializes a few internal * structures, incrementes the usage counter, and returns a pointer to it. * - * If no memory is available, NULL is returned. - * * If the driver want to use this urb for interrupt, control, or bulk * endpoints, pass '0' as the number of iso packets. * * The driver must call usb_free_urb() when it is finished with the urb. + * + * Return: A pointer to the new urb, or %NULL if no memory is available. */ struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags) { @@ -103,7 +103,7 @@ EXPORT_SYMBOL_GPL(usb_free_urb); * host controller driver. This allows proper reference counting to happen * for urbs. * - * A pointer to the urb with the incremented reference counter is returned. + * Return: A pointer to the urb with the incremented reference counter. */ struct urb *usb_get_urb(struct urb *urb) { @@ -200,13 +200,12 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb); * the particular kind of transfer, although they will not initialize * any transfer flags. * - * Successful submissions return 0; otherwise this routine returns a - * negative error number. If the submission is successful, the complete() - * callback from the URB will be called exactly once, when the USB core and - * Host Controller Driver (HCD) are finished with the URB. When the completion - * function is called, control of the URB is returned to the device - * driver which issued the request. The completion handler may then - * immediately free or reuse that URB. + * If the submission is successful, the complete() callback from the URB + * will be called exactly once, when the USB core and Host Controller Driver + * (HCD) are finished with the URB. When the completion function is called, + * control of the URB is returned to the device driver which issued the + * request. The completion handler may then immediately free or reuse that + * URB. * * With few exceptions, USB device drivers should never access URB fields * provided by usbcore or the HCD until its complete() is called. @@ -241,6 +240,9 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb); * that are standardized in the USB 2.0 specification. For bulk * endpoints, a synchronous usb_bulk_msg() call is available. * + * Return: + * 0 on successful submissions. A negative error number otherwise. + * * Request Queuing: * * URBs may be submitted to endpoints before previous ones complete, to @@ -572,6 +574,9 @@ EXPORT_SYMBOL_GPL(usb_submit_urb); * particular, when a driver calls this routine, it must insure that the * completion handler cannot deallocate the URB. * + * Return: -EINPROGRESS on success. See description for other values on + * failure. + * * Unlinking and Endpoint Queues: * * [The behaviors and guarantees described below do not apply to virtual @@ -846,6 +851,8 @@ EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs); * * Call this is you want to be sure all an anchor's * URBs have finished + * + * Return: Non-zero if the anchor became unused. Zero on timeout. */ int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, unsigned int timeout) @@ -859,8 +866,11 @@ EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout); * usb_get_from_anchor - get an anchor's oldest urb * @anchor: the anchor whose urb you want * - * this will take the oldest urb from an anchor, + * This will take the oldest urb from an anchor, * unanchor and return it + * + * Return: The oldest urb from @anchor, or %NULL if @anchor has no + * urbs associated with it. */ struct urb *usb_get_from_anchor(struct usb_anchor *anchor) { @@ -909,7 +919,7 @@ EXPORT_SYMBOL_GPL(usb_scuttle_anchored_urbs); * usb_anchor_empty - is an anchor empty * @anchor: the anchor you want to query * - * returns 1 if the anchor has no urbs associated with it + * Return: 1 if the anchor has no urbs associated with it. */ int usb_anchor_empty(struct usb_anchor *anchor) { diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 7dad603dde43..0a6ee2e70b25 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -68,6 +68,8 @@ MODULE_PARM_DESC(autosuspend, "default autosuspend delay"); * @alt_num: alternate interface setting number to search for. * * Search the configuration's interface cache for the given alt setting. + * + * Return: The alternate setting, if found. %NULL otherwise. */ struct usb_host_interface *usb_find_alt_setting( struct usb_host_config *config, @@ -103,8 +105,7 @@ EXPORT_SYMBOL_GPL(usb_find_alt_setting); * @ifnum: the desired interface * * This walks the device descriptor for the currently active configuration - * and returns a pointer to the interface with that particular interface - * number, or null. + * to find the interface object with the particular interface number. * * Note that configuration descriptors are not required to assign interface * numbers sequentially, so that it would be incorrect to assume that @@ -115,6 +116,9 @@ EXPORT_SYMBOL_GPL(usb_find_alt_setting); * * Don't call this function unless you are bound to one of the interfaces * on this device or you have locked the device! + * + * Return: A pointer to the interface that has @ifnum as interface number, + * if found. %NULL otherwise. */ struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev, unsigned ifnum) @@ -139,8 +143,7 @@ EXPORT_SYMBOL_GPL(usb_ifnum_to_if); * @altnum: the desired alternate setting number * * This searches the altsetting array of the specified interface for - * an entry with the correct bAlternateSetting value and returns a pointer - * to that entry, or null. + * an entry with the correct bAlternateSetting value. * * Note that altsettings need not be stored sequentially by number, so * it would be incorrect to assume that the first altsetting entry in @@ -149,6 +152,9 @@ EXPORT_SYMBOL_GPL(usb_ifnum_to_if); * * Don't call this function unless you are bound to the intf interface * or you have locked the device! + * + * Return: A pointer to the entry of the altsetting array of @intf that + * has @altnum as the alternate setting number. %NULL if not found. */ struct usb_host_interface *usb_altnum_to_altsetting( const struct usb_interface *intf, @@ -191,6 +197,8 @@ static int __find_interface(struct device *dev, void *data) * This walks the bus device list and returns a pointer to the interface * with the matching minor and driver. Note, this only works for devices * that share the USB major number. + * + * Return: A pointer to the interface with the matching major and @minor. */ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) { @@ -390,6 +398,9 @@ static unsigned usb_bus_is_wusb(struct usb_bus *bus) * controllers) should ever call this. * * This call may not be used in a non-sleeping context. + * + * Return: On success, a pointer to the allocated usb device. %NULL on + * failure. */ struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) @@ -501,7 +512,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, * their probe() methods, when they bind to an interface, and release * them by calling usb_put_dev(), in their disconnect() methods. * - * A pointer to the device with the incremented reference counter is returned. + * Return: A pointer to the device with the incremented reference counter. */ struct usb_device *usb_get_dev(struct usb_device *dev) { @@ -535,8 +546,7 @@ EXPORT_SYMBOL_GPL(usb_put_dev); * their probe() methods, when they bind to an interface, and release * them by calling usb_put_intf(), in their disconnect() methods. * - * A pointer to the interface with the incremented reference counter is - * returned. + * Return: A pointer to the interface with the incremented reference counter. */ struct usb_interface *usb_get_intf(struct usb_interface *intf) { @@ -589,7 +599,7 @@ EXPORT_SYMBOL_GPL(usb_put_intf); * disconnect; in some drivers (such as usb-storage) the disconnect() * or suspend() method will block waiting for a device reset to complete. * - * Returns a negative error code for failure, otherwise 0. + * Return: A negative error code for failure, otherwise 0. */ int usb_lock_device_for_reset(struct usb_device *udev, const struct usb_interface *iface) @@ -628,14 +638,15 @@ EXPORT_SYMBOL_GPL(usb_lock_device_for_reset); * usb_get_current_frame_number - return current bus frame number * @dev: the device whose bus is being queried * - * Returns the current frame number for the USB host controller - * used with the given USB device. This can be used when scheduling + * Return: The current frame number for the USB host controller used + * with the given USB device. This can be used when scheduling * isochronous requests. * - * Note that different kinds of host controller have different - * "scheduling horizons". While one type might support scheduling only - * 32 frames into the future, others could support scheduling up to - * 1024 frames into the future. + * Note: Different kinds of host controller have different "scheduling + * horizons". While one type might support scheduling only 32 frames + * into the future, others could support scheduling up to 1024 frames + * into the future. + * */ int usb_get_current_frame_number(struct usb_device *dev) { @@ -685,11 +696,12 @@ EXPORT_SYMBOL_GPL(__usb_get_extra_descriptor); * @mem_flags: affect whether allocation may block * @dma: used to return DMA address of buffer * - * Return value is either null (indicating no buffer could be allocated), or - * the cpu-space pointer to a buffer that may be used to perform DMA to the + * Return: Either null (indicating no buffer could be allocated), or the + * cpu-space pointer to a buffer that may be used to perform DMA to the * specified device. Such cpu-space buffers are returned along with the DMA * address (through the pointer provided). * + * Note: * These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags * to avoid behaviors like using "DMA bounce buffers", or thrashing IOMMU * hardware during URB completion/resubmit. The implementation varies between @@ -735,17 +747,18 @@ EXPORT_SYMBOL_GPL(usb_free_coherent); * usb_buffer_map - create DMA mapping(s) for an urb * @urb: urb whose transfer_buffer/setup_packet will be mapped * - * Return value is either null (indicating no buffer could be mapped), or - * the parameter. URB_NO_TRANSFER_DMA_MAP is - * added to urb->transfer_flags if the operation succeeds. If the device - * is connected to this system through a non-DMA controller, this operation - * always succeeds. + * URB_NO_TRANSFER_DMA_MAP is added to urb->transfer_flags if the operation + * succeeds. If the device is connected to this system through a non-DMA + * controller, this operation always succeeds. * * This call would normally be used for an urb which is reused, perhaps * as the target of a large periodic transfer, with usb_buffer_dmasync() * calls to synchronize memory and dma state. * * Reverse the effect of this call with usb_buffer_unmap(). + * + * Return: Either %NULL (indicating no buffer could be mapped), or @urb. + * */ #if 0 struct urb *usb_buffer_map(struct urb *urb) @@ -850,9 +863,10 @@ EXPORT_SYMBOL_GPL(usb_buffer_unmap); * @sg: the scatterlist to map * @nents: the number of entries in the scatterlist * - * Return value is either < 0 (indicating no buffers could be mapped), or - * the number of DMA mapping array entries in the scatterlist. + * Return: Either < 0 (indicating no buffers could be mapped), or the + * number of DMA mapping array entries in the scatterlist. * + * Note: * The caller is responsible for placing the resulting DMA addresses from * the scatterlist into URB transfer buffer pointers, and for setting the * URB_NO_TRANSFER_DMA_MAP transfer flag in each of those URBs. diff --git a/include/linux/usb.h b/include/linux/usb.h index 6efdab089014..acb1b0aa77d8 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -708,7 +708,10 @@ extern int usb_driver_claim_interface(struct usb_driver *driver, * usb_interface_claimed - returns true iff an interface is claimed * @iface: the interface being checked * - * Returns true (nonzero) iff the interface is claimed, else false (zero). + * Return: %true (nonzero) iff the interface is claimed, else %false + * (zero). + * + * Note: * Callers must own the driver model's usb bus readlock. So driver * probe() entries don't need extra locking, but other call contexts * may need to explicitly claim that lock. @@ -745,8 +748,9 @@ extern struct usb_host_interface *usb_find_alt_setting( * @buf: where to put the string * @size: how big is "buf"? * - * Returns length of the string (> 0) or negative if size was too small. + * Return: Length of the string (> 0) or negative if size was too small. * + * Note: * This identifier is intended to be "stable", reflecting physical paths in * hardware such as physical bus addresses for host controllers or ports on * USB hubs. That makes it stay the same until systems are physically @@ -1578,7 +1582,7 @@ extern int usb_anchor_empty(struct usb_anchor *anchor); * usb_urb_dir_in - check if an URB describes an IN transfer * @urb: URB to be checked * - * Returns 1 if @urb describes an IN transfer (device-to-host), + * Return: 1 if @urb describes an IN transfer (device-to-host), * otherwise 0. */ static inline int usb_urb_dir_in(struct urb *urb) @@ -1590,7 +1594,7 @@ static inline int usb_urb_dir_in(struct urb *urb) * usb_urb_dir_out - check if an URB describes an OUT transfer * @urb: URB to be checked * - * Returns 1 if @urb describes an OUT transfer (host-to-device), + * Return: 1 if @urb describes an OUT transfer (host-to-device), * otherwise 0. */ static inline int usb_urb_dir_out(struct urb *urb) -- cgit v1.2.3-71-gd317 From 73f5698e77219bfc3ea1903759fe8e20ab5b285e Mon Sep 17 00:00:00 2001 From: Stefan Tomanek Date: Sat, 3 Aug 2013 14:14:43 +0200 Subject: fib_rules: fix suppressor names and default values This change brings the suppressor attribute names into line; it also changes the data types to provide a more consistent interface. While -1 indicates that the suppressor is not enabled, values >= 0 for suppress_prefixlen or suppress_ifgroup reject routing decisions violating the constraint. This changes the previously presented behaviour of suppress_prefixlen, where a prefix length _less_ than the attribute value was rejected. After this change, a prefix length less than *or* equal to the value is considered a violation of the rule constraint. It also changes the default values for default and newly added rules (disabling any suppression for those). Signed-off-by: Stefan Tomanek Signed-off-by: David S. Miller --- include/net/fib_rules.h | 4 ++-- include/uapi/linux/fib_rules.h | 2 +- net/core/fib_rules.c | 15 +++++++++++---- net/ipv4/fib_rules.c | 2 +- net/ipv6/fib6_rules.c | 2 +- 5 files changed, 16 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index d13c461b4b59..9d0fcbaa9cbb 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -19,7 +19,7 @@ struct fib_rule { u32 flags; u32 table; int suppress_ifgroup; - u8 table_prefixlen_min; + int suppress_prefixlen; u8 action; u32 target; struct fib_rule __rcu *ctarget; @@ -84,7 +84,7 @@ struct fib_rules_ops { [FRA_FWMARK] = { .type = NLA_U32 }, \ [FRA_FWMASK] = { .type = NLA_U32 }, \ [FRA_TABLE] = { .type = NLA_U32 }, \ - [FRA_TABLE_PREFIXLEN_MIN] = { .type = NLA_U8 }, \ + [FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32 }, \ [FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32 }, \ [FRA_GOTO] = { .type = NLA_U32 } diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h index 63e31166e85b..2b82d7e30974 100644 --- a/include/uapi/linux/fib_rules.h +++ b/include/uapi/linux/fib_rules.h @@ -45,7 +45,7 @@ enum { FRA_FLOW, /* flow/class id */ FRA_UNUSED6, FRA_SUPPRESS_IFGROUP, - FRA_TABLE_PREFIXLEN_MIN, + FRA_SUPPRESS_PREFIXLEN, FRA_TABLE, /* Extended table id */ FRA_FWMASK, /* mask for netfilter mark */ FRA_OIFNAME, diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 5040a61bf28a..2e654138433c 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -33,6 +33,9 @@ int fib_default_rule_add(struct fib_rules_ops *ops, r->flags = flags; r->fr_net = hold_net(ops->fro_net); + r->suppress_prefixlen = -1; + r->suppress_ifgroup = -1; + /* The lock is not required here, the list in unreacheable * at the moment this function is called */ list_add_tail(&r->list, &ops->rules_list); @@ -340,11 +343,15 @@ static int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh) rule->action = frh->action; rule->flags = frh->flags; rule->table = frh_get_table(frh, tb); - if (tb[FRA_TABLE_PREFIXLEN_MIN]) - rule->table_prefixlen_min = nla_get_u8(tb[FRA_TABLE_PREFIXLEN_MIN]); + if (tb[FRA_SUPPRESS_PREFIXLEN]) + rule->suppress_prefixlen = nla_get_u32(tb[FRA_SUPPRESS_PREFIXLEN]); + else + rule->suppress_prefixlen = -1; if (tb[FRA_SUPPRESS_IFGROUP]) rule->suppress_ifgroup = nla_get_u32(tb[FRA_SUPPRESS_IFGROUP]); + else + rule->suppress_ifgroup = -1; if (!tb[FRA_PRIORITY] && ops->default_pref) rule->pref = ops->default_pref(ops); @@ -531,7 +538,7 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops, + nla_total_size(IFNAMSIZ) /* FRA_OIFNAME */ + nla_total_size(4) /* FRA_PRIORITY */ + nla_total_size(4) /* FRA_TABLE */ - + nla_total_size(1) /* FRA_TABLE_PREFIXLEN_MIN */ + + nla_total_size(4) /* FRA_SUPPRESS_PREFIXLEN */ + nla_total_size(4) /* FRA_SUPPRESS_IFGROUP */ + nla_total_size(4) /* FRA_FWMARK */ + nla_total_size(4); /* FRA_FWMASK */ @@ -558,7 +565,7 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule, frh->table = rule->table; if (nla_put_u32(skb, FRA_TABLE, rule->table)) goto nla_put_failure; - if (nla_put_u8(skb, FRA_TABLE_PREFIXLEN_MIN, rule->table_prefixlen_min)) + if (nla_put_u32(skb, FRA_SUPPRESS_PREFIXLEN, rule->suppress_prefixlen)) goto nla_put_failure; frh->res1 = 0; frh->res2 = 0; diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index b78fd28970c9..523be38e37de 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -109,7 +109,7 @@ static bool fib4_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg *arg /* do not accept result if the route does * not meet the required prefix length */ - if (result->prefixlen < rule->table_prefixlen_min) + if (result->prefixlen <= rule->suppress_prefixlen) goto suppress_route; /* do not accept result if the route uses a device diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index 36283267e2f8..a6c58ce43d34 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -126,7 +126,7 @@ static bool fib6_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg *arg /* do not accept result if the route does * not meet the required prefix length */ - if (rt->rt6i_dst.plen < rule->table_prefixlen_min) + if (rt->rt6i_dst.plen <= rule->suppress_prefixlen) goto suppress_route; /* do not accept result if the route uses a device -- cgit v1.2.3-71-gd317 From 23cde4d65cc7d11e2048d2b240cdf13927ac50d0 Mon Sep 17 00:00:00 2001 From: Denis CIOCCA Date: Wed, 19 Jun 2013 09:28:00 +0100 Subject: iio: Added ST-sensors platform data to select the DRDY interrupt pin This patch add support to redirect the DRDY interrupt on INT1 or INT2 on accelerometer and pressure sensors. Signed-off-by: Denis Ciocca Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel.h | 11 ++++++- drivers/iio/accel/st_accel_core.c | 27 +++++++++++----- drivers/iio/accel/st_accel_i2c.c | 2 +- drivers/iio/accel/st_accel_spi.c | 2 +- drivers/iio/common/st_sensors/st_sensors_core.c | 41 ++++++++++++++++++++++--- drivers/iio/gyro/st_gyro.h | 11 ++++++- drivers/iio/gyro/st_gyro_core.c | 13 ++++---- drivers/iio/gyro/st_gyro_i2c.c | 3 +- drivers/iio/gyro/st_gyro_spi.c | 3 +- drivers/iio/magnetometer/st_magn.h | 3 +- drivers/iio/magnetometer/st_magn_core.c | 5 +-- drivers/iio/magnetometer/st_magn_i2c.c | 2 +- drivers/iio/magnetometer/st_magn_spi.c | 2 +- drivers/iio/pressure/st_pressure.h | 11 ++++++- drivers/iio/pressure/st_pressure_core.c | 15 ++++++--- drivers/iio/pressure/st_pressure_i2c.c | 2 +- drivers/iio/pressure/st_pressure_spi.c | 2 +- include/linux/iio/common/st_sensors.h | 14 +++++++-- include/linux/platform_data/st_sensors_pdata.h | 24 +++++++++++++++ 19 files changed, 153 insertions(+), 40 deletions(-) create mode 100644 include/linux/platform_data/st_sensors_pdata.h (limited to 'include') diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 37949b94377d..c3877630b2e4 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -25,7 +25,16 @@ #define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel" #define LSM330_ACCEL_DEV_NAME "lsm330_accel" -int st_accel_common_probe(struct iio_dev *indio_dev); +/** +* struct st_sensors_platform_data - default accel platform data +* @drdy_int_pin: default accel DRDY is available on INT1 pin. +*/ +static const struct st_sensors_platform_data default_accel_pdata = { + .drdy_int_pin = 1, +}; + +int st_accel_common_probe(struct iio_dev *indio_dev, + struct st_sensors_platform_data *pdata); void st_accel_common_remove(struct iio_dev *indio_dev); #ifdef CONFIG_IIO_BUFFER diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 4aec121261d7..aef3c9be7366 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -65,7 +65,8 @@ #define ST_ACCEL_1_BDU_ADDR 0x23 #define ST_ACCEL_1_BDU_MASK 0x80 #define ST_ACCEL_1_DRDY_IRQ_ADDR 0x22 -#define ST_ACCEL_1_DRDY_IRQ_MASK 0x10 +#define ST_ACCEL_1_DRDY_IRQ_INT1_MASK 0x10 +#define ST_ACCEL_1_DRDY_IRQ_INT2_MASK 0x08 #define ST_ACCEL_1_MULTIREAD_BIT true /* CUSTOM VALUES FOR SENSOR 2 */ @@ -89,7 +90,8 @@ #define ST_ACCEL_2_BDU_ADDR 0x23 #define ST_ACCEL_2_BDU_MASK 0x80 #define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22 -#define ST_ACCEL_2_DRDY_IRQ_MASK 0x02 +#define ST_ACCEL_2_DRDY_IRQ_INT1_MASK 0x02 +#define ST_ACCEL_2_DRDY_IRQ_INT2_MASK 0x10 #define ST_ACCEL_2_MULTIREAD_BIT true /* CUSTOM VALUES FOR SENSOR 3 */ @@ -121,7 +123,8 @@ #define ST_ACCEL_3_BDU_ADDR 0x20 #define ST_ACCEL_3_BDU_MASK 0x08 #define ST_ACCEL_3_DRDY_IRQ_ADDR 0x23 -#define ST_ACCEL_3_DRDY_IRQ_MASK 0x80 +#define ST_ACCEL_3_DRDY_IRQ_INT1_MASK 0x80 +#define ST_ACCEL_3_DRDY_IRQ_INT2_MASK 0x00 #define ST_ACCEL_3_IG1_EN_ADDR 0x23 #define ST_ACCEL_3_IG1_EN_MASK 0x08 #define ST_ACCEL_3_MULTIREAD_BIT false @@ -224,7 +227,8 @@ static const struct st_sensors st_accel_sensors[] = { }, .drdy_irq = { .addr = ST_ACCEL_1_DRDY_IRQ_ADDR, - .mask = ST_ACCEL_1_DRDY_IRQ_MASK, + .mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK, + .mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_MASK, }, .multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT, .bootime = 2, @@ -285,7 +289,8 @@ static const struct st_sensors st_accel_sensors[] = { }, .drdy_irq = { .addr = ST_ACCEL_2_DRDY_IRQ_ADDR, - .mask = ST_ACCEL_2_DRDY_IRQ_MASK, + .mask_int1 = ST_ACCEL_2_DRDY_IRQ_INT1_MASK, + .mask_int2 = ST_ACCEL_2_DRDY_IRQ_INT2_MASK, }, .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT, .bootime = 2, @@ -358,7 +363,8 @@ static const struct st_sensors st_accel_sensors[] = { }, .drdy_irq = { .addr = ST_ACCEL_3_DRDY_IRQ_ADDR, - .mask = ST_ACCEL_3_DRDY_IRQ_MASK, + .mask_int1 = ST_ACCEL_3_DRDY_IRQ_INT1_MASK, + .mask_int2 = ST_ACCEL_3_DRDY_IRQ_INT2_MASK, .ig1 = { .en_addr = ST_ACCEL_3_IG1_EN_ADDR, .en_mask = ST_ACCEL_3_IG1_EN_MASK, @@ -443,7 +449,8 @@ static const struct iio_trigger_ops st_accel_trigger_ops = { #define ST_ACCEL_TRIGGER_OPS NULL #endif -int st_accel_common_probe(struct iio_dev *indio_dev) +int st_accel_common_probe(struct iio_dev *indio_dev, + struct st_sensors_platform_data *plat_data) { int err; struct st_sensor_data *adata = iio_priv(indio_dev); @@ -465,7 +472,11 @@ int st_accel_common_probe(struct iio_dev *indio_dev) &adata->sensor->fs.fs_avl[0]; adata->odr = adata->sensor->odr.odr_avl[0].hz; - err = st_sensors_init_sensor(indio_dev); + if (!plat_data) + plat_data = + (struct st_sensors_platform_data *)&default_accel_pdata; + + err = st_sensors_init_sensor(indio_dev, plat_data); if (err < 0) goto st_accel_common_probe_error; diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index ffc9d097e484..58d164d90dca 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -36,7 +36,7 @@ static int st_accel_i2c_probe(struct i2c_client *client, st_sensors_i2c_configure(indio_dev, client, adata); - err = st_accel_common_probe(indio_dev); + err = st_accel_common_probe(indio_dev, client->dev.platform_data); if (err < 0) goto st_accel_common_probe_error; diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 22b35bfea7d2..21ed9296311f 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -35,7 +35,7 @@ static int st_accel_spi_probe(struct spi_device *spi) st_sensors_spi_configure(indio_dev, spi, adata); - err = st_accel_common_probe(indio_dev); + err = st_accel_common_probe(indio_dev, spi->dev.platform_data); if (err < 0) goto st_accel_common_probe_error; diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 865b1781df66..965ee22d3ac8 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -22,7 +22,7 @@ static inline u32 st_sensors_get_unaligned_le24(const u8 *p) { - return ((s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8); + return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8; } static int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, @@ -118,7 +118,7 @@ st_sensors_match_odr_error: } static int st_sensors_set_fullscale(struct iio_dev *indio_dev, - unsigned int fs) + unsigned int fs) { int err, i = 0; struct st_sensor_data *sdata = iio_priv(indio_dev); @@ -198,13 +198,39 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable) } EXPORT_SYMBOL(st_sensors_set_axis_enable); -int st_sensors_init_sensor(struct iio_dev *indio_dev) +int st_sensors_init_sensor(struct iio_dev *indio_dev, + struct st_sensors_platform_data *pdata) { int err; struct st_sensor_data *sdata = iio_priv(indio_dev); mutex_init(&sdata->tb.buf_lock); + switch (pdata->drdy_int_pin) { + case 1: + if (sdata->sensor->drdy_irq.mask_int1 == 0) { + dev_err(&indio_dev->dev, + "DRDY on INT1 not available.\n"); + err = -EINVAL; + goto init_error; + } + sdata->drdy_int_pin = 1; + break; + case 2: + if (sdata->sensor->drdy_irq.mask_int2 == 0) { + dev_err(&indio_dev->dev, + "DRDY on INT2 not available.\n"); + err = -EINVAL; + goto init_error; + } + sdata->drdy_int_pin = 2; + break; + default: + dev_err(&indio_dev->dev, "DRDY on pdata not valid.\n"); + err = -EINVAL; + goto init_error; + } + err = st_sensors_set_enable(indio_dev, false); if (err < 0) goto init_error; @@ -234,6 +260,7 @@ EXPORT_SYMBOL(st_sensors_init_sensor); int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable) { int err; + u8 drdy_mask; struct st_sensor_data *sdata = iio_priv(indio_dev); /* Enable/Disable the interrupt generator 1. */ @@ -245,10 +272,14 @@ int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable) goto st_accel_set_dataready_irq_error; } + if (sdata->drdy_int_pin == 1) + drdy_mask = sdata->sensor->drdy_irq.mask_int1; + else + drdy_mask = sdata->sensor->drdy_irq.mask_int2; + /* Enable/Disable the interrupt generator for data ready. */ err = st_sensors_write_data_with_mask(indio_dev, - sdata->sensor->drdy_irq.addr, - sdata->sensor->drdy_irq.mask, (int)enable); + sdata->sensor->drdy_irq.addr, drdy_mask, (int)enable); st_accel_set_dataready_irq_error: return err; diff --git a/drivers/iio/gyro/st_gyro.h b/drivers/iio/gyro/st_gyro.h index 3ad9907bb154..f8f2bf84a5a2 100644 --- a/drivers/iio/gyro/st_gyro.h +++ b/drivers/iio/gyro/st_gyro.h @@ -23,7 +23,16 @@ #define L3G4IS_GYRO_DEV_NAME "l3g4is_ui" #define LSM330_GYRO_DEV_NAME "lsm330_gyro" -int st_gyro_common_probe(struct iio_dev *indio_dev); +/** + * struct st_sensors_platform_data - gyro platform data + * @drdy_int_pin: DRDY on gyros is available only on INT2 pin. + */ +static const struct st_sensors_platform_data gyro_pdata = { + .drdy_int_pin = 2, +}; + +int st_gyro_common_probe(struct iio_dev *indio_dev, + struct st_sensors_platform_data *pdata); void st_gyro_common_remove(struct iio_dev *indio_dev); #ifdef CONFIG_IIO_BUFFER diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index f9ed3488c314..85fa8d343bb2 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -60,7 +60,7 @@ #define ST_GYRO_1_BDU_ADDR 0x23 #define ST_GYRO_1_BDU_MASK 0x80 #define ST_GYRO_1_DRDY_IRQ_ADDR 0x22 -#define ST_GYRO_1_DRDY_IRQ_MASK 0x08 +#define ST_GYRO_1_DRDY_IRQ_INT2_MASK 0x08 #define ST_GYRO_1_MULTIREAD_BIT true /* CUSTOM VALUES FOR SENSOR 2 */ @@ -84,7 +84,7 @@ #define ST_GYRO_2_BDU_ADDR 0x23 #define ST_GYRO_2_BDU_MASK 0x80 #define ST_GYRO_2_DRDY_IRQ_ADDR 0x22 -#define ST_GYRO_2_DRDY_IRQ_MASK 0x08 +#define ST_GYRO_2_DRDY_IRQ_INT2_MASK 0x08 #define ST_GYRO_2_MULTIREAD_BIT true static const struct iio_chan_spec st_gyro_16bit_channels[] = { @@ -158,7 +158,7 @@ static const struct st_sensors st_gyro_sensors[] = { }, .drdy_irq = { .addr = ST_GYRO_1_DRDY_IRQ_ADDR, - .mask = ST_GYRO_1_DRDY_IRQ_MASK, + .mask_int2 = ST_GYRO_1_DRDY_IRQ_INT2_MASK, }, .multi_read_bit = ST_GYRO_1_MULTIREAD_BIT, .bootime = 2, @@ -221,7 +221,7 @@ static const struct st_sensors st_gyro_sensors[] = { }, .drdy_irq = { .addr = ST_GYRO_2_DRDY_IRQ_ADDR, - .mask = ST_GYRO_2_DRDY_IRQ_MASK, + .mask_int2 = ST_GYRO_2_DRDY_IRQ_INT2_MASK, }, .multi_read_bit = ST_GYRO_2_MULTIREAD_BIT, .bootime = 2, @@ -302,7 +302,8 @@ static const struct iio_trigger_ops st_gyro_trigger_ops = { #define ST_GYRO_TRIGGER_OPS NULL #endif -int st_gyro_common_probe(struct iio_dev *indio_dev) +int st_gyro_common_probe(struct iio_dev *indio_dev, + struct st_sensors_platform_data *pdata) { int err; struct st_sensor_data *gdata = iio_priv(indio_dev); @@ -324,7 +325,7 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) &gdata->sensor->fs.fs_avl[0]; gdata->odr = gdata->sensor->odr.odr_avl[0].hz; - err = st_sensors_init_sensor(indio_dev); + err = st_sensors_init_sensor(indio_dev, pdata); if (err < 0) goto st_gyro_common_probe_error; diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index 8a310500573d..c7a29a4d7e82 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -36,7 +36,8 @@ static int st_gyro_i2c_probe(struct i2c_client *client, st_sensors_i2c_configure(indio_dev, client, gdata); - err = st_gyro_common_probe(indio_dev); + err = st_gyro_common_probe(indio_dev, + (struct st_sensors_platform_data *)&gyro_pdata); if (err < 0) goto st_gyro_common_probe_error; diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index f3540390eb22..14b0762847f5 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -35,7 +35,8 @@ static int st_gyro_spi_probe(struct spi_device *spi) st_sensors_spi_configure(indio_dev, spi, gdata); - err = st_gyro_common_probe(indio_dev); + err = st_gyro_common_probe(indio_dev, + (struct st_sensors_platform_data *)&gyro_pdata); if (err < 0) goto st_gyro_common_probe_error; diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h index 7e81d00ef0c3..694e33e0fb72 100644 --- a/drivers/iio/magnetometer/st_magn.h +++ b/drivers/iio/magnetometer/st_magn.h @@ -18,7 +18,8 @@ #define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn" #define LIS3MDL_MAGN_DEV_NAME "lis3mdl" -int st_magn_common_probe(struct iio_dev *indio_dev); +int st_magn_common_probe(struct iio_dev *indio_dev, + struct st_sensors_platform_data *pdata); void st_magn_common_remove(struct iio_dev *indio_dev); #ifdef CONFIG_IIO_BUFFER diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index ebfe8f11a0c2..7cd784f522e5 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -345,7 +345,8 @@ static const struct iio_info magn_info = { .write_raw = &st_magn_write_raw, }; -int st_magn_common_probe(struct iio_dev *indio_dev) +int st_magn_common_probe(struct iio_dev *indio_dev, + struct st_sensors_platform_data *pdata) { int err; struct st_sensor_data *mdata = iio_priv(indio_dev); @@ -367,7 +368,7 @@ int st_magn_common_probe(struct iio_dev *indio_dev) &mdata->sensor->fs.fs_avl[0]; mdata->odr = mdata->sensor->odr.odr_avl[0].hz; - err = st_sensors_init_sensor(indio_dev); + err = st_sensors_init_sensor(indio_dev, pdata); if (err < 0) goto st_magn_common_probe_error; diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index e6adc4a86425..1bed117714c5 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -36,7 +36,7 @@ static int st_magn_i2c_probe(struct i2c_client *client, st_sensors_i2c_configure(indio_dev, client, mdata); - err = st_magn_common_probe(indio_dev); + err = st_magn_common_probe(indio_dev, NULL); if (err < 0) goto st_magn_common_probe_error; diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index 51adb797cb7d..a2333a1e6061 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -35,7 +35,7 @@ static int st_magn_spi_probe(struct spi_device *spi) st_sensors_spi_configure(indio_dev, spi, mdata); - err = st_magn_common_probe(indio_dev); + err = st_magn_common_probe(indio_dev, NULL); if (err < 0) goto st_magn_common_probe_error; diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h index 414e45ac9b9b..b0b630688da6 100644 --- a/drivers/iio/pressure/st_pressure.h +++ b/drivers/iio/pressure/st_pressure.h @@ -16,7 +16,16 @@ #define LPS331AP_PRESS_DEV_NAME "lps331ap" -int st_press_common_probe(struct iio_dev *indio_dev); +/** + * struct st_sensors_platform_data - default press platform data + * @drdy_int_pin: default press DRDY is available on INT1 pin. + */ +static const struct st_sensors_platform_data default_press_pdata = { + .drdy_int_pin = 1, +}; + +int st_press_common_probe(struct iio_dev *indio_dev, + struct st_sensors_platform_data *pdata); void st_press_common_remove(struct iio_dev *indio_dev); #ifdef CONFIG_IIO_BUFFER diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 3ffbc56917b4..81e2d5b030a6 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -58,7 +58,8 @@ #define ST_PRESS_1_BDU_ADDR 0x20 #define ST_PRESS_1_BDU_MASK 0x04 #define ST_PRESS_1_DRDY_IRQ_ADDR 0x22 -#define ST_PRESS_1_DRDY_IRQ_MASK 0x04 +#define ST_PRESS_1_DRDY_IRQ_INT1_MASK 0x04 +#define ST_PRESS_1_DRDY_IRQ_INT2_MASK 0x20 #define ST_PRESS_1_MULTIREAD_BIT true #define ST_PRESS_1_TEMP_OFFSET 42500 @@ -116,7 +117,8 @@ static const struct st_sensors st_press_sensors[] = { }, .drdy_irq = { .addr = ST_PRESS_1_DRDY_IRQ_ADDR, - .mask = ST_PRESS_1_DRDY_IRQ_MASK, + .mask_int1 = ST_PRESS_1_DRDY_IRQ_INT1_MASK, + .mask_int2 = ST_PRESS_1_DRDY_IRQ_INT2_MASK, }, .multi_read_bit = ST_PRESS_1_MULTIREAD_BIT, .bootime = 2, @@ -202,7 +204,8 @@ static const struct iio_trigger_ops st_press_trigger_ops = { #define ST_PRESS_TRIGGER_OPS NULL #endif -int st_press_common_probe(struct iio_dev *indio_dev) +int st_press_common_probe(struct iio_dev *indio_dev, + struct st_sensors_platform_data *plat_data) { int err; struct st_sensor_data *pdata = iio_priv(indio_dev); @@ -224,7 +227,11 @@ int st_press_common_probe(struct iio_dev *indio_dev) &pdata->sensor->fs.fs_avl[0]; pdata->odr = pdata->sensor->odr.odr_avl[0].hz; - err = st_sensors_init_sensor(indio_dev); + if (!plat_data) + plat_data = + (struct st_sensors_platform_data *)&default_press_pdata; + + err = st_sensors_init_sensor(indio_dev, plat_data); if (err < 0) goto st_press_common_probe_error; diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 7cebcc73bfb0..306599307a96 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -36,7 +36,7 @@ static int st_press_i2c_probe(struct i2c_client *client, st_sensors_i2c_configure(indio_dev, client, pdata); - err = st_press_common_probe(indio_dev); + err = st_press_common_probe(indio_dev, client->dev.platform_data); if (err < 0) goto st_press_common_probe_error; diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index 17a14907940a..b2aded6d2108 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -35,7 +35,7 @@ static int st_press_spi_probe(struct spi_device *spi) st_sensors_spi_configure(indio_dev, spi, pdata); - err = st_press_common_probe(indio_dev); + err = st_press_common_probe(indio_dev, spi->dev.platform_data); if (err < 0) goto st_press_common_probe_error; diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index 72b26940730d..e51f65480ea5 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -17,6 +17,8 @@ #include #include +#include + #define ST_SENSORS_TX_MAX_LENGTH 2 #define ST_SENSORS_RX_MAX_LENGTH 6 @@ -118,14 +120,16 @@ struct st_sensor_bdu { /** * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt * @addr: address of the register. - * @mask: mask to write the on/off value. + * @mask_int1: mask to enable/disable IRQ on INT1 pin. + * @mask_int2: mask to enable/disable IRQ on INT2 pin. * struct ig1 - represents the Interrupt Generator 1 of sensors. * @en_addr: address of the enable ig1 register. * @en_mask: mask to write the on/off value for enable. */ struct st_sensor_data_ready_irq { u8 addr; - u8 mask; + u8 mask_int1; + u8 mask_int2; struct { u8 en_addr; u8 en_mask; @@ -201,6 +205,7 @@ struct st_sensors { * @buffer_data: Data used by buffer part. * @odr: Output data rate of the sensor [Hz]. * num_data_channels: Number of data channels used in buffer. + * @drdy_int_pin: Redirect DRDY on pin 1 (1) or pin 2 (2). * @get_irq_data_ready: Function to get the IRQ used for data ready signal. * @tf: Transfer function structure used by I/O operations. * @tb: Transfer buffers and mutex used by I/O operations. @@ -219,6 +224,8 @@ struct st_sensor_data { unsigned int odr; unsigned int num_data_channels; + u8 drdy_int_pin; + unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev); const struct st_sensor_transfer_function *tf; @@ -249,7 +256,8 @@ static inline void st_sensors_deallocate_trigger(struct iio_dev *indio_dev) } #endif -int st_sensors_init_sensor(struct iio_dev *indio_dev); +int st_sensors_init_sensor(struct iio_dev *indio_dev, + struct st_sensors_platform_data *pdata); int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable); diff --git a/include/linux/platform_data/st_sensors_pdata.h b/include/linux/platform_data/st_sensors_pdata.h new file mode 100644 index 000000000000..753839187ba0 --- /dev/null +++ b/include/linux/platform_data/st_sensors_pdata.h @@ -0,0 +1,24 @@ +/* + * STMicroelectronics sensors platform-data driver + * + * Copyright 2013 STMicroelectronics Inc. + * + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#ifndef ST_SENSORS_PDATA_H +#define ST_SENSORS_PDATA_H + +/** + * struct st_sensors_platform_data - Platform data for the ST sensors + * @drdy_int_pin: Redirect DRDY on pin 1 (1) or pin 2 (2). + * Available only for accelerometer and pressure sensors. + * Accelerometer DRDY on LSM330 available only on pin 1 (see datasheet). + */ +struct st_sensors_platform_data { + u8 drdy_int_pin; +}; + +#endif /* ST_SENSORS_PDATA_H */ -- cgit v1.2.3-71-gd317 From 60b188f0d679656a1a2603d3bc1fa005373f4b9c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 16 Jul 2013 16:08:00 +0100 Subject: iio: Remove IIO_DEV_ATTR_RESET macro There are no users of this macro left and we have come to the conclusion that it is not a good idea to expose the raw chip reset to userspace so the macro is very unlikely to be used in new drivers. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- include/linux/iio/sysfs.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'include') diff --git a/include/linux/iio/sysfs.h b/include/linux/iio/sysfs.h index b7a934b9431b..2958c960003a 100644 --- a/include/linux/iio/sysfs.h +++ b/include/linux/iio/sysfs.h @@ -73,11 +73,6 @@ struct iio_const_attr { .dev_attr = __ATTR(_name, S_IRUGO, iio_read_const_attr, NULL)} /* Generic attributes of onetype or another */ -/** - * IIO_DEV_ATTR_RESET: resets the device - **/ -#define IIO_DEV_ATTR_RESET(_store) \ - IIO_DEVICE_ATTR(reset, S_IWUSR, NULL, _store, 0) /** * IIO_DEV_ATTR_SAMP_FREQ - sets any internal clock frequency -- cgit v1.2.3-71-gd317 From 9dabaf5eddbafa21aded7c063cb38d2e8936c237 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Thu, 18 Jul 2013 11:19:00 +0100 Subject: iio: core: implement devm_iio_device_alloc/devm_iio_device_free Add a resource managed devm_iio_device_alloc()/devm_iio_device_free() to automatically clean up any allocations made by IIO drivers, thus leading to simplified IIO drivers code. In addition, this will allow IIO drivers to use other devm_*() API (like devm_request_irq) and don't care about the race between iio_device_free() and the release of resources by Device core during driver removing. Signed-off-by: Grygorii Strashko Signed-off-by: Oleksandr Kravchenko Tested-by: Oleksandr Kravchenko Reviewed-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 47 +++++++++++++++++++++++++++++++++++++++++ include/linux/iio/iio.h | 25 ++++++++++++++++++++++ 2 files changed, 72 insertions(+) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index e145931ef1b8..d56d1229e2d6 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -912,6 +912,53 @@ void iio_device_free(struct iio_dev *dev) } EXPORT_SYMBOL(iio_device_free); +static void devm_iio_device_release(struct device *dev, void *res) +{ + iio_device_free(*(struct iio_dev **)res); +} + +static int devm_iio_device_match(struct device *dev, void *res, void *data) +{ + struct iio_dev **r = res; + if (!r || !*r) { + WARN_ON(!r || !*r); + return 0; + } + return *r == data; +} + +struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv) +{ + struct iio_dev **ptr, *iio_dev; + + ptr = devres_alloc(devm_iio_device_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return NULL; + + /* use raw alloc_dr for kmalloc caller tracing */ + iio_dev = iio_device_alloc(sizeof_priv); + if (iio_dev) { + *ptr = iio_dev; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return iio_dev; +} +EXPORT_SYMBOL_GPL(devm_iio_device_alloc); + +void devm_iio_device_free(struct device *dev, struct iio_dev *iio_dev) +{ + int rc; + + rc = devres_release(dev, devm_iio_device_release, + devm_iio_device_match, iio_dev); + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_iio_device_free); + /** * iio_chrdev_open() - chrdev file open for buffer access and ioctls **/ diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 3d35b7023591..61d220f89aca 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -531,6 +531,31 @@ static inline struct iio_dev *iio_priv_to_dev(void *priv) **/ void iio_device_free(struct iio_dev *indio_dev); +/** + * devm_iio_device_alloc - Resource-managed iio_device_alloc() + * @dev: Device to allocate iio_dev for + * @sizeof_priv: Space to allocate for private structure. + * + * Managed iio_device_alloc. iio_dev allocated with this function is + * automatically freed on driver detach. + * + * If an iio_dev allocated with this function needs to be freed separately, + * devm_iio_device_free() must be used. + * + * RETURNS: + * Pointer to allocated iio_dev on success, NULL on failure. + */ +struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv); + +/** + * devm_iio_device_free - Resource-managed iio_device_free() + * @dev: Device this iio_dev belongs to + * @indio_dev: the iio_dev associated with the device + * + * Free indio_dev allocated with devm_iio_device_alloc(). + */ +void devm_iio_device_free(struct device *dev, struct iio_dev *iio_dev); + /** * iio_buffer_enabled() - helper function to test if the buffer is enabled * @indio_dev: IIO device structure for device -- cgit v1.2.3-71-gd317 From fba3679d34511c42bf452e89dda457a1219eb43a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 3 Aug 2013 11:50:35 -0700 Subject: fib_rules: reorder struct fib_rules fields Move refcnt, pref, suppress_ifgroup, suppress_prefixlen out of first cache line, as they are not used in fast path. Make sure ctarget & fr_net are in first cache line. (Assuming 64 bit arches and 64 bytes cache lines) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/fib_rules.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 9d0fcbaa9cbb..4b2b557fb0e8 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -10,23 +10,25 @@ struct fib_rule { struct list_head list; - atomic_t refcnt; int iifindex; int oifindex; u32 mark; u32 mark_mask; - u32 pref; u32 flags; u32 table; - int suppress_ifgroup; - int suppress_prefixlen; u8 action; + /* 3 bytes hole, try to use */ u32 target; struct fib_rule __rcu *ctarget; + struct net *fr_net; + + atomic_t refcnt; + u32 pref; + int suppress_ifgroup; + int suppress_prefixlen; char iifname[IFNAMSIZ]; char oifname[IFNAMSIZ]; struct rcu_head rcu; - struct net * fr_net; }; struct fib_lookup_arg { -- cgit v1.2.3-71-gd317 From 9bc541028b2b93962e0bebce62ce6e295aab2086 Mon Sep 17 00:00:00 2001 From: Peter Meerwald Date: Mon, 29 Jul 2013 22:35:00 +0100 Subject: iio: Fix documentation of devm_iio_device_free() Signed-off-by: Peter Meerwald Cc: Grygorii Strashko Cc: Oleksandr Kravchenko Signed-off-by: Jonathan Cameron --- include/linux/iio/iio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 61d220f89aca..09ebe0a4d8e6 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -552,9 +552,9 @@ struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv); * @dev: Device this iio_dev belongs to * @indio_dev: the iio_dev associated with the device * - * Free indio_dev allocated with devm_iio_device_alloc(). + * Free iio_dev allocated with devm_iio_device_alloc(). */ -void devm_iio_device_free(struct device *dev, struct iio_dev *iio_dev); +void devm_iio_device_free(struct device *dev, struct iio_dev *indio_dev); /** * iio_buffer_enabled() - helper function to test if the buffer is enabled -- cgit v1.2.3-71-gd317 From 6eee5b3b493824731ed34ade0299241f91f04096 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Tue, 11 Jun 2013 20:48:02 +0900 Subject: extcon: Add an API to get extcon device from dt node Added an API of_extcon_get_extcon_dev() to be used by drivers to get extcon device in the case of dt boot (this can be used instead of extcon_get_extcon_dev()). Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- drivers/extcon/Kconfig | 4 +++ drivers/extcon/Makefile | 2 ++ drivers/extcon/extcon-class.c | 3 +- drivers/extcon/of_extcon.c | 64 ++++++++++++++++++++++++++++++++++++++++ include/linux/extcon/of_extcon.h | 31 +++++++++++++++++++ 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 drivers/extcon/of_extcon.c create mode 100644 include/linux/extcon/of_extcon.h (limited to 'include') diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig index 63f454e20576..f1d54a3985bd 100644 --- a/drivers/extcon/Kconfig +++ b/drivers/extcon/Kconfig @@ -14,6 +14,10 @@ if EXTCON comment "Extcon Device Drivers" +config OF_EXTCON + def_tristate y + depends on OF + config EXTCON_GPIO tristate "GPIO extcon support" depends on GPIOLIB diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile index 540e2c3a4431..759fdae46f95 100644 --- a/drivers/extcon/Makefile +++ b/drivers/extcon/Makefile @@ -2,6 +2,8 @@ # Makefile for external connector class (extcon) devices # +obj-$(CONFIG_OF_EXTCON) += of_extcon.o + obj-$(CONFIG_EXTCON) += extcon-class.o obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o diff --git a/drivers/extcon/extcon-class.c b/drivers/extcon/extcon-class.c index 18ccadef43fd..1446152bf2e9 100644 --- a/drivers/extcon/extcon-class.c +++ b/drivers/extcon/extcon-class.c @@ -602,7 +602,8 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev) edev->dev->class = extcon_class; edev->dev->release = extcon_dev_release; - dev_set_name(edev->dev, "%s", edev->name ? edev->name : dev_name(dev)); + edev->name = edev->name ? edev->name : dev_name(dev); + dev_set_name(edev->dev, "%s", edev->name); if (edev->max_supported) { char buf[10]; diff --git a/drivers/extcon/of_extcon.c b/drivers/extcon/of_extcon.c new file mode 100644 index 000000000000..72173ecbb311 --- /dev/null +++ b/drivers/extcon/of_extcon.c @@ -0,0 +1,64 @@ +/* + * OF helpers for External connector (extcon) framework + * + * Copyright (C) 2013 Texas Instruments, Inc. + * Kishon Vijay Abraham I + * + * Copyright (C) 2013 Samsung Electronics + * Chanwoo Choi + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * of_extcon_get_extcon_dev - Get the name of extcon device from devicetree + * @dev - instance to the given device + * @index - index into list of extcon_dev + * + * return the instance of extcon device + */ +struct extcon_dev *of_extcon_get_extcon_dev(struct device *dev, int index) +{ + struct device_node *node; + struct extcon_dev *edev; + struct platform_device *extcon_parent_dev; + + if (!dev->of_node) { + dev_dbg(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + node = of_parse_phandle(dev->of_node, "extcon", index); + if (!node) { + dev_dbg(dev, "failed to get phandle in %s node\n", + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + + extcon_parent_dev = of_find_device_by_node(node); + if (!extcon_parent_dev) { + dev_dbg(dev, "unable to find device by node\n"); + return ERR_PTR(-EPROBE_DEFER); + } + + edev = extcon_get_extcon_dev(dev_name(&extcon_parent_dev->dev)); + if (!edev) { + dev_dbg(dev, "unable to get extcon device : %s\n", + dev_name(&extcon_parent_dev->dev)); + return ERR_PTR(-ENODEV); + } + + return edev; +} +EXPORT_SYMBOL_GPL(of_extcon_get_extcon_dev); diff --git a/include/linux/extcon/of_extcon.h b/include/linux/extcon/of_extcon.h new file mode 100644 index 000000000000..0ebfeff1b55d --- /dev/null +++ b/include/linux/extcon/of_extcon.h @@ -0,0 +1,31 @@ +/* + * OF helpers for External connector (extcon) framework + * + * Copyright (C) 2013 Texas Instruments, Inc. + * Kishon Vijay Abraham I + * + * Copyright (C) 2013 Samsung Electronics + * Chanwoo Choi + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __LINUX_OF_EXTCON_H +#define __LINUX_OF_EXTCON_H + +#include + +#if IS_ENABLED(CONFIG_OF_EXTCON) +extern struct extcon_dev + *of_extcon_get_extcon_dev(struct device *dev, int index); +#else +static inline struct extcon_dev + *of_extcon_get_extcon_dev(struct device *dev, int index) +{ + return ERR_PTR(-ENOSYS); +} +#endif /* CONFIG_OF_EXTCON */ +#endif /* __LINUX_OF_EXTCON_H */ -- cgit v1.2.3-71-gd317 From 8061ad7239f3f97b477984660e95134ca684578c Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 8 Jul 2013 09:54:43 +0900 Subject: usb: dwc3: use extcon fwrk to receive connect/disconnect Modified dwc3-omap to receive connect and disconnect notification using extcon framework. Also did the necessary cleanups required after adapting to extcon framework. Signed-off-by: Kishon Vijay Abraham I Acked-by: Felipe Balbi Signed-off-by: Chanwoo Choi --- Documentation/devicetree/bindings/usb/omap-usb.txt | 5 + drivers/usb/dwc3/Kconfig | 1 + drivers/usb/dwc3/dwc3-omap.c | 125 +++++++++++++++++---- include/linux/usb/dwc3-omap.h | 30 ----- 4 files changed, 112 insertions(+), 49 deletions(-) delete mode 100644 include/linux/usb/dwc3-omap.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt index 57e71f6817d0..9088ab09e200 100644 --- a/Documentation/devicetree/bindings/usb/omap-usb.txt +++ b/Documentation/devicetree/bindings/usb/omap-usb.txt @@ -53,6 +53,11 @@ OMAP DWC3 GLUE It should be set to "1" for HW mode and "2" for SW mode. - ranges: the child address space are mapped 1:1 onto the parent address space +Optional Properties: + - extcon : phandle for the extcon device omap dwc3 uses to detect + connect/disconnect events. + - vbus-supply : phandle to the regulator device tree node if needed. + Sub-nodes: The dwc3 core should be added as subnode to omap dwc3 glue. - dwc3 : diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 2378958ea63e..4c855e598c84 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -1,6 +1,7 @@ config USB_DWC3 tristate "DesignWare USB3 DRD Core Support" depends on (USB || USB_GADGET) && GENERIC_HARDIRQS && HAS_DMA + depends on EXTCON select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD help Say Y or M here if your system has a Dual Role SuperSpeed diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 077f110bd746..b26c2a4b4fb5 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -43,13 +43,15 @@ #include #include #include -#include #include #include #include #include #include #include +#include +#include +#include #include @@ -155,9 +157,21 @@ struct dwc3_omap { u32 revision; u32 dma_status:1; + + struct extcon_specific_cable_nb extcon_vbus_dev; + struct extcon_specific_cable_nb extcon_id_dev; + struct notifier_block vbus_nb; + struct notifier_block id_nb; + + struct regulator *vbus_reg; }; -static struct dwc3_omap *_omap; +enum omap_dwc3_vbus_id_status { + OMAP_DWC3_ID_FLOAT, + OMAP_DWC3_ID_GROUND, + OMAP_DWC3_VBUS_OFF, + OMAP_DWC3_VBUS_VALID, +}; static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset) { @@ -221,18 +235,24 @@ static void dwc3_omap_write_irq0_set(struct dwc3_omap *omap, u32 value) omap->irq0_offset, value); } -int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) +static void dwc3_omap_set_mailbox(struct dwc3_omap *omap, + enum omap_dwc3_vbus_id_status status) { - u32 val; - struct dwc3_omap *omap = _omap; - - if (!omap) - return -EPROBE_DEFER; + int ret; + u32 val; switch (status) { case OMAP_DWC3_ID_GROUND: dev_dbg(omap->dev, "ID GND\n"); + if (omap->vbus_reg) { + ret = regulator_enable(omap->vbus_reg); + if (ret) { + dev_dbg(omap->dev, "regulator enable failed\n"); + return; + } + } + val = dwc3_omap_read_utmi_status(omap); val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID @@ -255,6 +275,9 @@ int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) break; case OMAP_DWC3_ID_FLOAT: + if (omap->vbus_reg) + regulator_disable(omap->vbus_reg); + case OMAP_DWC3_VBUS_OFF: dev_dbg(omap->dev, "VBUS Disconnect\n"); @@ -268,12 +291,9 @@ int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) break; default: - dev_dbg(omap->dev, "ID float\n"); + dev_dbg(omap->dev, "invalid state\n"); } - - return 0; } -EXPORT_SYMBOL_GPL(dwc3_omap_mailbox); static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) { @@ -366,6 +386,32 @@ static void dwc3_omap_disable_irqs(struct dwc3_omap *omap) static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32); +static int dwc3_omap_id_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct dwc3_omap *omap = container_of(nb, struct dwc3_omap, id_nb); + + if (event) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND); + else + dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_FLOAT); + + return NOTIFY_DONE; +} + +static int dwc3_omap_vbus_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct dwc3_omap *omap = container_of(nb, struct dwc3_omap, vbus_nb); + + if (event) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID); + else + dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_OFF); + + return NOTIFY_DONE; +} + static int dwc3_omap_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; @@ -373,6 +419,8 @@ static int dwc3_omap_probe(struct platform_device *pdev) struct dwc3_omap *omap; struct resource *res; struct device *dev = &pdev->dev; + struct extcon_dev *edev; + struct regulator *vbus_reg = NULL; int ret = -ENOMEM; int irq; @@ -415,19 +463,22 @@ static int dwc3_omap_probe(struct platform_device *pdev) return -ENOMEM; } + if (of_property_read_bool(node, "vbus-supply")) { + vbus_reg = devm_regulator_get(dev, "vbus"); + if (IS_ERR(vbus_reg)) { + dev_err(dev, "vbus init failed\n"); + return PTR_ERR(vbus_reg); + } + } + spin_lock_init(&omap->lock); omap->dev = dev; omap->irq = irq; omap->base = base; + omap->vbus_reg = vbus_reg; dev->dma_mask = &dwc3_omap_dma_mask; - /* - * REVISIT if we ever have two instances of the wrapper, we will be - * in big trouble - */ - _omap = omap; - pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) { @@ -502,14 +553,46 @@ static int dwc3_omap_probe(struct platform_device *pdev) dwc3_omap_enable_irqs(omap); + if (of_property_read_bool(node, "extcon")) { + edev = of_extcon_get_extcon_dev(dev, 0); + if (IS_ERR(edev)) { + dev_vdbg(dev, "couldn't get extcon device\n"); + ret = PTR_ERR(edev); + goto err2; + } + + omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier; + ret = extcon_register_interest(&omap->extcon_vbus_dev, + edev->name, "USB", &omap->vbus_nb); + if (ret < 0) + dev_vdbg(dev, "failed to register notifier for USB\n"); + omap->id_nb.notifier_call = dwc3_omap_id_notifier; + ret = extcon_register_interest(&omap->extcon_id_dev, edev->name, + "USB-HOST", &omap->id_nb); + if (ret < 0) + dev_vdbg(dev, + "failed to register notifier for USB-HOST\n"); + + if (extcon_get_cable_state(edev, "USB") == true) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID); + if (extcon_get_cable_state(edev, "USB-HOST") == true) + dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND); + } + ret = of_platform_populate(node, NULL, NULL, dev); if (ret) { dev_err(&pdev->dev, "failed to create dwc3 core\n"); - goto err2; + goto err3; } return 0; +err3: + if (omap->extcon_vbus_dev.edev) + extcon_unregister_interest(&omap->extcon_vbus_dev); + if (omap->extcon_id_dev.edev) + extcon_unregister_interest(&omap->extcon_id_dev); + err2: dwc3_omap_disable_irqs(omap); @@ -526,6 +609,10 @@ static int dwc3_omap_remove(struct platform_device *pdev) { struct dwc3_omap *omap = platform_get_drvdata(pdev); + if (omap->extcon_vbus_dev.edev) + extcon_unregister_interest(&omap->extcon_vbus_dev); + if (omap->extcon_id_dev.edev) + extcon_unregister_interest(&omap->extcon_id_dev); dwc3_omap_disable_irqs(omap); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/include/linux/usb/dwc3-omap.h b/include/linux/usb/dwc3-omap.h deleted file mode 100644 index 5615f4d82724..000000000000 --- a/include/linux/usb/dwc3-omap.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2013 by Texas Instruments - * - * The Inventra Controller Driver for Linux is free software; you - * can redistribute it and/or modify it under the terms of the GNU - * General Public License version 2 as published by the Free Software - * Foundation. - */ - -#ifndef __DWC3_OMAP_H__ -#define __DWC3_OMAP_H__ - -enum omap_dwc3_vbus_id_status { - OMAP_DWC3_UNKNOWN = 0, - OMAP_DWC3_ID_GROUND, - OMAP_DWC3_ID_FLOAT, - OMAP_DWC3_VBUS_VALID, - OMAP_DWC3_VBUS_OFF, -}; - -#if (defined(CONFIG_USB_DWC3) || defined(CONFIG_USB_DWC3_MODULE)) -extern int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status); -#else -static inline int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status) -{ - return -ENODEV; -} -#endif - -#endif /* __DWC3_OMAP_H__ */ -- cgit v1.2.3-71-gd317 From 43e3f38892a6a521ff5901cdb97cf6985101badd Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 9 Jul 2013 18:34:21 +0530 Subject: extcon: palmas: remove unused member from palams_usb structure Remove unused member vbus_enable and set_vbus_work from palmas_usb as these members are not used in drivers. Signed-off-by: Laxman Dewangan Acked-by: Graeme Gregory Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- include/linux/mfd/palmas.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 1a8dd7afe084..03c22ca55446 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -371,16 +371,11 @@ struct palmas_usb { struct extcon_dev edev; - /* used to set vbus, in atomic path */ - struct work_struct set_vbus_work; - int id_otg_irq; int id_irq; int vbus_otg_irq; int vbus_irq; - int vbus_enable; - enum palmas_usb_state linkstat; }; -- cgit v1.2.3-71-gd317 From 7281e05aabee7484624d448da208932690ed62b3 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 10 Jul 2013 14:59:06 +0530 Subject: extcon: palmas: Option to disable ID/VBUS detection based on platform Based on system design, platform needs to detect the VBUS or ID or both. Provide option to select this through platform data to disable part of cable detection through palmas-usb. Signed-off-by: Laxman Dewangan Acked-by: Graeme Gregory Signed-off-by: Chanwoo Choi Signed-off-by: Myungjoo Ham --- .../devicetree/bindings/extcon/extcon-palmas.txt | 2 + drivers/extcon/extcon-palmas.c | 87 +++++++++++++--------- include/linux/mfd/palmas.h | 3 + 3 files changed, 57 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/extcon/extcon-palmas.txt b/Documentation/devicetree/bindings/extcon/extcon-palmas.txt index f110e75fd0a1..7dab6a8f4a0e 100644 --- a/Documentation/devicetree/bindings/extcon/extcon-palmas.txt +++ b/Documentation/devicetree/bindings/extcon/extcon-palmas.txt @@ -6,6 +6,8 @@ Required Properties: Optional Properties: - ti,wakeup : To enable the wakeup comparator in probe + - ti,enable-id-detection: Perform ID detection. + - ti,enable-vbus-detection: Perform VBUS detection. palmas-usb { compatible = "ti,twl6035-usb", "ti,palmas-usb"; diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index d0ce0f16de70..89fdd05c5fd6 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -122,11 +122,14 @@ static void palmas_enable_irq(struct palmas_usb *palmas_usb) PALMAS_USB_ID_INT_EN_HI_SET_ID_GND | PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT); - palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb); + if (palmas_usb->enable_vbus_detection) + palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb); /* cold plug for host mode needs this delay */ - msleep(30); - palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb); + if (palmas_usb->enable_id_detection) { + msleep(30); + palmas_id_irq_handler(palmas_usb->id_irq, palmas_usb); + } } static int palmas_usb_probe(struct platform_device *pdev) @@ -137,21 +140,25 @@ static int palmas_usb_probe(struct platform_device *pdev) struct palmas_usb *palmas_usb; int status; - if (node && !pdata) { - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - - if (!pdata) - return -ENOMEM; - - pdata->wakeup = of_property_read_bool(node, "ti,wakeup"); - } else if (!pdata) { - return -EINVAL; - } - palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL); if (!palmas_usb) return -ENOMEM; + if (node && !pdata) { + palmas_usb->wakeup = of_property_read_bool(node, "ti,wakeup"); + palmas_usb->enable_id_detection = of_property_read_bool(node, + "ti,enable-id-detection"); + palmas_usb->enable_vbus_detection = of_property_read_bool(node, + "ti,enable-vbus-detection"); + } else { + palmas_usb->wakeup = true; + palmas_usb->enable_id_detection = true; + palmas_usb->enable_vbus_detection = true; + + if (pdata) + palmas_usb->wakeup = pdata->wakeup; + } + palmas->usb = palmas_usb; palmas_usb->palmas = palmas; @@ -166,7 +173,7 @@ static int palmas_usb_probe(struct platform_device *pdev) palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data, PALMAS_VBUS_IRQ); - palmas_usb_wakeup(palmas, pdata->wakeup); + palmas_usb_wakeup(palmas, palmas_usb->wakeup); platform_set_drvdata(pdev, palmas_usb); @@ -179,26 +186,32 @@ static int palmas_usb_probe(struct platform_device *pdev) return status; } - status = devm_request_threaded_irq(palmas_usb->dev, palmas_usb->id_irq, - NULL, palmas_id_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | - IRQF_ONESHOT | IRQF_EARLY_RESUME, - "palmas_usb_id", palmas_usb); - if (status < 0) { - dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + if (palmas_usb->enable_id_detection) { + status = devm_request_threaded_irq(palmas_usb->dev, + palmas_usb->id_irq, + NULL, palmas_id_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT | IRQF_EARLY_RESUME, + "palmas_usb_id", palmas_usb); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", palmas_usb->id_irq, status); - goto fail_extcon; + goto fail_extcon; + } } - status = devm_request_threaded_irq(palmas_usb->dev, - palmas_usb->vbus_irq, NULL, palmas_vbus_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | - IRQF_ONESHOT | IRQF_EARLY_RESUME, - "palmas_usb_vbus", palmas_usb); - if (status < 0) { - dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", + if (palmas_usb->enable_vbus_detection) { + status = devm_request_threaded_irq(palmas_usb->dev, + palmas_usb->vbus_irq, NULL, + palmas_vbus_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT | IRQF_EARLY_RESUME, + "palmas_usb_vbus", palmas_usb); + if (status < 0) { + dev_err(&pdev->dev, "can't get IRQ %d, err %d\n", palmas_usb->vbus_irq, status); - goto fail_extcon; + goto fail_extcon; + } } palmas_enable_irq(palmas_usb); @@ -226,8 +239,10 @@ static int palmas_usb_suspend(struct device *dev) struct palmas_usb *palmas_usb = dev_get_drvdata(dev); if (device_may_wakeup(dev)) { - enable_irq_wake(palmas_usb->vbus_irq); - enable_irq_wake(palmas_usb->id_irq); + if (palmas_usb->enable_vbus_detection) + enable_irq_wake(palmas_usb->vbus_irq); + if (palmas_usb->enable_id_detection) + enable_irq_wake(palmas_usb->id_irq); } return 0; } @@ -237,8 +252,10 @@ static int palmas_usb_resume(struct device *dev) struct palmas_usb *palmas_usb = dev_get_drvdata(dev); if (device_may_wakeup(dev)) { - disable_irq_wake(palmas_usb->vbus_irq); - disable_irq_wake(palmas_usb->id_irq); + if (palmas_usb->enable_vbus_detection) + disable_irq_wake(palmas_usb->vbus_irq); + if (palmas_usb->enable_id_detection) + disable_irq_wake(palmas_usb->id_irq); } return 0; }; diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 03c22ca55446..cfc678ceb570 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -377,6 +377,9 @@ struct palmas_usb { int vbus_irq; enum palmas_usb_state linkstat; + int wakeup; + bool enable_vbus_detection; + bool enable_id_detection; }; #define comparator_to_palmas(x) container_of((x), struct palmas_usb, comparator) -- cgit v1.2.3-71-gd317 From e473fcb472574de978e47f980aeca510020a1286 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Wed, 26 Jun 2013 23:56:58 +0200 Subject: xfrm: constify mark argument of xfrm_find_acq() The mark argument is read only, so constify it. Also make dummy_mark in af_key const -- only used as dummy argument for this very function. Signed-off-by: Mathias Krause Cc: "David S. Miller" Cc: Herbert Xu Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 2 +- net/key/af_key.c | 2 +- net/xfrm/xfrm_state.c | 12 +++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 94ce082b29dc..89d3d8ae204e 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1548,7 +1548,7 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir, u32 int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info); u32 xfrm_get_acqseq(void); extern int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi); -struct xfrm_state *xfrm_find_acq(struct net *net, struct xfrm_mark *mark, +struct xfrm_state *xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid, u8 proto, const xfrm_address_t *daddr, const xfrm_address_t *saddr, int create, diff --git a/net/key/af_key.c b/net/key/af_key.c index ab8bd2cabfa0..4089a210e852 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -45,7 +45,7 @@ struct netns_pfkey { static DEFINE_MUTEX(pfkey_mutex); #define DUMMY_MARK 0 -static struct xfrm_mark dummy_mark = {0, 0}; +static const struct xfrm_mark dummy_mark = {0, 0}; struct pfkey_sock { /* struct sock must be the first member of struct pfkey_sock */ struct sock sk; diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 78f66fa92449..b2cd806c0be3 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -990,11 +990,13 @@ void xfrm_state_insert(struct xfrm_state *x) EXPORT_SYMBOL(xfrm_state_insert); /* xfrm_state_lock is held */ -static struct xfrm_state *__find_acq_core(struct net *net, struct xfrm_mark *m, +static struct xfrm_state *__find_acq_core(struct net *net, + const struct xfrm_mark *m, unsigned short family, u8 mode, u32 reqid, u8 proto, const xfrm_address_t *daddr, - const xfrm_address_t *saddr, int create) + const xfrm_address_t *saddr, + int create) { unsigned int h = xfrm_dst_hash(net, daddr, saddr, reqid, family); struct xfrm_state *x; @@ -1399,9 +1401,9 @@ xfrm_state_lookup_byaddr(struct net *net, u32 mark, EXPORT_SYMBOL(xfrm_state_lookup_byaddr); struct xfrm_state * -xfrm_find_acq(struct net *net, struct xfrm_mark *mark, u8 mode, u32 reqid, u8 proto, - const xfrm_address_t *daddr, const xfrm_address_t *saddr, - int create, unsigned short family) +xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid, + u8 proto, const xfrm_address_t *daddr, + const xfrm_address_t *saddr, int create, unsigned short family) { struct xfrm_state *x; -- cgit v1.2.3-71-gd317 From 57295073b6acfdfaf9319d3caf92a5c433fdf109 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 5 Aug 2013 11:27:31 +0200 Subject: ASoC: dapm: Implement mixer input auto-disable Some devices have the problem that if a internal audio signal source is disabled the output of the source becomes undefined or goes to a undesired state (E.g. DAC output goes to ground instead of VMID). In this case it is necessary, in order to avoid unwanted clicks and pops, to disable any mixer input the signal feeds into or to active a mute control along the path to the output. Often it is still desirable to expose the same mixer input control to userspace, so cerain paths can sill be disabled manually. This means we can not use conventional DAPM to manage the mixer input control. This patch implements a method for letting DAPM overwrite the state of a userspace visible control. I.e. DAPM will disable the control if the path on which the control sits becomes inactive. Userspace will then only see a cached copy of the controls state. Once DAPM powers the path up again it will sync the userspace setting with the hardware and give control back to userspace. To implement this a new widget type is introduced. One widget of this type will be created for each DAPM kcontrol which has the auto-disable feature enabled. For each path that is controlled by the kcontrol the widget will be connected to the source of that path. The new widget type behaves like a supply widget, which means it will power up if one of its sinks are powered up and will only power down if all of its sinks are powered down. In order to only have the mixer input enabled when the source signal is valid the new widget type will be disabled before all other widget types and only be enabled after all other widget types. E.g. consider the following simplified example. A DAC is connected to a mixer and the mixer has a control to enable or disable the signal from the DAC. +-------+ +-----+ | | | DAC |-----[Ctrl]-| Mixer | +-----+ : | | | : +-------+ | : +-------------+ | Ctrl widget | +-------------+ If the control has the auto-disable feature enabled we'll create a widget for the control. This widget is connected to the DAC as it is the source for the mixer input. If the DAC powers up the control widget powers up and if the DAC powers down the control widget is powered down. As long as the control widget is powered down the hardware input control is kept disabled and if it is enabled userspace can freely change the control's state. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 17 +++++++- include/sound/soc.h | 28 ++++++------ sound/soc/soc-dapm.c | 108 +++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 125 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 3575721a955d..c728d28ae9a5 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -280,14 +280,26 @@ struct device; { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } +#define SOC_DAPM_SINGLE_AUTODISABLE(xname, reg, shift, max, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 1) } #define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ .tlv.p = (tlv_array), \ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } +#define SOC_DAPM_SINGLE_TLV_AUTODISABLE(xname, reg, shift, max, invert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_volsw, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } #define SOC_DAPM_ENUM(xname, xenum) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_enum_double, \ @@ -484,6 +496,7 @@ enum snd_soc_dapm_type { snd_soc_dapm_dai_in, /* link to DAI structure */ snd_soc_dapm_dai_out, snd_soc_dapm_dai_link, /* link between two DAI structures */ + snd_soc_dapm_kcontrol, /* Auto-disabled kcontrol */ }; enum snd_soc_dapm_subclass { diff --git a/include/sound/soc.h b/include/sound/soc.h index b1e1f967ae1e..6201c6ede8ba 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -30,13 +30,13 @@ /* * Convenience kcontrol builders */ -#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert) \ +#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert, xautodisable) \ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xreg, .rreg = xreg, .shift = shift_left, \ .rshift = shift_right, .max = xmax, .platform_max = xmax, \ - .invert = xinvert}) -#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \ - SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert) + .invert = xinvert, .autodisable = xautodisable}) +#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \ + SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable) #define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \ ((unsigned long)&(struct soc_mixer_control) \ {.reg = xreg, .max = xmax, .platform_max = xmax, .invert = xinvert}) @@ -52,7 +52,7 @@ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = snd_soc_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } #define SOC_SINGLE_RANGE(xname, xreg, xshift, xmin, xmax, xinvert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .info = snd_soc_info_volsw_range, .get = snd_soc_get_volsw_range, \ @@ -68,7 +68,7 @@ .tlv.p = (tlv_array), \ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\ .put = snd_soc_put_volsw, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) } #define SOC_SINGLE_SX_TLV(xname, xreg, xshift, xmin, xmax, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ @@ -97,7 +97,7 @@ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \ - max, invert) } + max, invert, 0) } #define SOC_DOUBLE_R(xname, reg_left, reg_right, xshift, xmax, xinvert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ .info = snd_soc_info_volsw, \ @@ -119,7 +119,7 @@ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \ .put = snd_soc_put_volsw, \ .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \ - max, invert) } + max, invert, 0) } #define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ @@ -190,14 +190,14 @@ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ - .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) } + .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) } #define SOC_DOUBLE_EXT(xname, reg, shift_left, shift_right, max, invert,\ xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = \ - SOC_DOUBLE_VALUE(reg, shift_left, shift_right, max, invert) } + SOC_DOUBLE_VALUE(reg, shift_left, shift_right, max, invert, 0) } #define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -206,7 +206,7 @@ .tlv.p = (tlv_array), \ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ - .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) } + .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) } #define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ @@ -216,7 +216,7 @@ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, \ - xmax, xinvert) } + xmax, xinvert, 0) } #define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\ xhandler_get, xhandler_put, tlv_array) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ @@ -1088,7 +1088,9 @@ struct snd_soc_pcm_runtime { /* mixer control */ struct soc_mixer_control { int min, max, platform_max; - unsigned int reg, rreg, shift, rshift, invert; + unsigned int reg, rreg, shift, rshift; + unsigned int invert:1; + unsigned int autodisable:1; }; struct soc_bytes { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 5f64c16336ad..0944bc4bd4a4 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -47,6 +47,15 @@ #define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++; +static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, + struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink, + const char *control, + int (*connected)(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink)); +static struct snd_soc_dapm_widget * +snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, + const struct snd_soc_dapm_widget *widget); + /* dapm power sequences - make this per codec in the future */ static int dapm_up_seq[] = { [snd_soc_dapm_pre] = 0, @@ -73,16 +82,18 @@ static int dapm_up_seq[] = { [snd_soc_dapm_hp] = 10, [snd_soc_dapm_spk] = 10, [snd_soc_dapm_line] = 10, - [snd_soc_dapm_post] = 11, + [snd_soc_dapm_kcontrol] = 11, + [snd_soc_dapm_post] = 12, }; static int dapm_down_seq[] = { [snd_soc_dapm_pre] = 0, - [snd_soc_dapm_adc] = 1, - [snd_soc_dapm_hp] = 2, - [snd_soc_dapm_spk] = 2, - [snd_soc_dapm_line] = 2, - [snd_soc_dapm_out_drv] = 2, + [snd_soc_dapm_kcontrol] = 1, + [snd_soc_dapm_adc] = 2, + [snd_soc_dapm_hp] = 3, + [snd_soc_dapm_spk] = 3, + [snd_soc_dapm_line] = 3, + [snd_soc_dapm_out_drv] = 3, [snd_soc_dapm_pga] = 4, [snd_soc_dapm_switch] = 5, [snd_soc_dapm_mixer_named_ctl] = 5, @@ -176,6 +187,7 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget( struct dapm_kcontrol_data { unsigned int value; + struct snd_soc_dapm_widget *widget; struct list_head paths; struct snd_soc_dapm_widget_list *wlist; }; @@ -184,6 +196,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, struct snd_kcontrol *kcontrol) { struct dapm_kcontrol_data *data; + struct soc_mixer_control *mc; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { @@ -195,6 +208,39 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, INIT_LIST_HEAD(&data->paths); + switch (widget->id) { + case snd_soc_dapm_switch: + case snd_soc_dapm_mixer: + case snd_soc_dapm_mixer_named_ctl: + mc = (struct soc_mixer_control *)kcontrol->private_value; + + if (mc->autodisable) { + struct snd_soc_dapm_widget template; + + memset(&template, 0, sizeof(template)); + template.reg = mc->reg; + template.mask = (1 << fls(mc->max)) - 1; + template.shift = mc->shift; + if (mc->invert) + template.off_val = mc->max; + else + template.off_val = 0; + template.on_val = template.off_val; + template.id = snd_soc_dapm_kcontrol; + template.name = kcontrol->id.name; + + data->widget = snd_soc_dapm_new_control(widget->dapm, + &template); + if (!data->widget) { + kfree(data); + return -ENOMEM; + } + } + break; + default: + break; + } + kcontrol->private_data = data; return 0; @@ -203,6 +249,7 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget, static void dapm_kcontrol_free(struct snd_kcontrol *kctl) { struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl); + kfree(data->widget); kfree(data->wlist); kfree(data); } @@ -246,6 +293,21 @@ static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol, struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); list_add_tail(&path->list_kcontrol, &data->paths); + + if (data->widget) { + snd_soc_dapm_add_path(data->widget->dapm, data->widget, + path->source, NULL, NULL); + } +} + +static bool dapm_kcontrol_is_powered(const struct snd_kcontrol *kcontrol) +{ + struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol); + + if (!data->widget) + return true; + + return data->widget->power; } static struct list_head *dapm_kcontrol_get_path_list( @@ -275,6 +337,9 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol, if (data->value == value) return false; + if (data->widget) + data->widget->on_val = value; + data->value = value; return true; @@ -515,6 +580,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w, case snd_soc_dapm_spk: case snd_soc_dapm_line: case snd_soc_dapm_dai_link: + case snd_soc_dapm_kcontrol: p->connect = 1; break; /* does affect routing - dynamically connected */ @@ -880,6 +946,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: return 0; default: break; @@ -975,6 +1042,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: return 0; default: break; @@ -1523,7 +1591,7 @@ static void dapm_widget_update(struct snd_soc_card *card) unsigned int wi; int ret; - if (!update) + if (!update || !dapm_kcontrol_is_powered(update->kcontrol)) return; wlist = dapm_kcontrol_get_wlist(update->kcontrol); @@ -1668,6 +1736,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: /* Supplies can't affect their outputs, only their inputs */ break; default: @@ -2335,6 +2404,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: case snd_soc_dapm_dai_link: + case snd_soc_dapm_kcontrol: list_add(&path->list, &dapm->card->paths); list_add(&path->list_sink, &wsink->sources); list_add(&path->list_source, &wsource->sinks); @@ -2717,6 +2787,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->card; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; @@ -2724,17 +2795,24 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, int max = mc->max; unsigned int mask = (1 << fls(max)) - 1; unsigned int invert = mc->invert; + unsigned int val; if (snd_soc_volsw_is_stereo(mc)) dev_warn(codec->dapm.dev, "ASoC: Control '%s' is stereo, which is not supported\n", kcontrol->id.name); - ucontrol->value.integer.value[0] = - (snd_soc_read(codec, reg) >> shift) & mask; + mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + if (dapm_kcontrol_is_powered(kcontrol)) + val = (snd_soc_read(codec, reg) >> shift) & mask; + else + val = dapm_kcontrol_get_value(kcontrol); + mutex_unlock(&card->dapm_mutex); + if (invert) - ucontrol->value.integer.value[0] = - max - ucontrol->value.integer.value[0]; + ucontrol->value.integer.value[0] = max - val; + else + ucontrol->value.integer.value[0] = val; return 0; } @@ -2775,11 +2853,14 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, if (invert) val = max - val; - mask = mask << shift; - val = val << shift; mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + dapm_kcontrol_set_value(kcontrol, val); + + mask = mask << shift; + val = val << shift; + change = snd_soc_test_bits(codec, reg, mask, val); if (change) { update.kcontrol = kcontrol; @@ -3179,6 +3260,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, case snd_soc_dapm_supply: case snd_soc_dapm_regulator_supply: case snd_soc_dapm_clock_supply: + case snd_soc_dapm_kcontrol: w->power_check = dapm_supply_check_power; break; default: -- cgit v1.2.3-71-gd317 From 6cdd97821322352b94bb92f271b4466ad38a2468 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Mon, 5 Aug 2013 10:52:36 -0600 Subject: vfio: add external user support VFIO is designed to be used via ioctls on file descriptors returned by VFIO. However in some situations support for an external user is required. The first user is KVM on PPC64 (SPAPR TCE protocol) which is going to use the existing VFIO groups for exclusive access in real/virtual mode on a host to avoid passing map/unmap requests to the user space which would made things pretty slow. The protocol includes: 1. do normal VFIO init operation: - opening a new container; - attaching group(s) to it; - setting an IOMMU driver for a container. When IOMMU is set for a container, all groups in it are considered ready to use by an external user. 2. User space passes a group fd to an external user. The external user calls vfio_group_get_external_user() to verify that: - the group is initialized; - IOMMU is set for it. If both checks passed, vfio_group_get_external_user() increments the container user counter to prevent the VFIO group from disposal before KVM exits. 3. The external user calls vfio_external_user_iommu_id() to know an IOMMU ID. PPC64 KVM uses it to link logical bus number (LIOBN) with IOMMU ID. 4. When the external KVM finishes, it calls vfio_group_put_external_user() to release the VFIO group. This call decrements the container user counter. Everything gets released. The "vfio: Limit group opens" patch is also required for the consistency. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alex Williamson --- drivers/vfio/vfio.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/vfio.h | 7 ++++++ 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 842f4507883e..d3cb34224369 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -1352,6 +1352,68 @@ static const struct file_operations vfio_device_fops = { .mmap = vfio_device_fops_mmap, }; +/** + * External user API, exported by symbols to be linked dynamically. + * + * The protocol includes: + * 1. do normal VFIO init operation: + * - opening a new container; + * - attaching group(s) to it; + * - setting an IOMMU driver for a container. + * When IOMMU is set for a container, all groups in it are + * considered ready to use by an external user. + * + * 2. User space passes a group fd to an external user. + * The external user calls vfio_group_get_external_user() + * to verify that: + * - the group is initialized; + * - IOMMU is set for it. + * If both checks passed, vfio_group_get_external_user() + * increments the container user counter to prevent + * the VFIO group from disposal before KVM exits. + * + * 3. The external user calls vfio_external_user_iommu_id() + * to know an IOMMU ID. + * + * 4. When the external KVM finishes, it calls + * vfio_group_put_external_user() to release the VFIO group. + * This call decrements the container user counter. + */ +struct vfio_group *vfio_group_get_external_user(struct file *filep) +{ + struct vfio_group *group = filep->private_data; + + if (filep->f_op != &vfio_group_fops) + return ERR_PTR(-EINVAL); + + if (!atomic_inc_not_zero(&group->container_users)) + return ERR_PTR(-EINVAL); + + if (!group->container->iommu_driver || + !vfio_group_viable(group)) { + atomic_dec(&group->container_users); + return ERR_PTR(-EINVAL); + } + + vfio_group_get(group); + + return group; +} +EXPORT_SYMBOL_GPL(vfio_group_get_external_user); + +void vfio_group_put_external_user(struct vfio_group *group) +{ + vfio_group_put(group); + vfio_group_try_dissolve_container(group); +} +EXPORT_SYMBOL_GPL(vfio_group_put_external_user); + +int vfio_external_user_iommu_id(struct vfio_group *group) +{ + return iommu_group_id(group->iommu_group); +} +EXPORT_SYMBOL_GPL(vfio_external_user_iommu_id); + /** * Module/class support */ diff --git a/include/linux/vfio.h b/include/linux/vfio.h index ac8d488e4372..24579a0312a0 100644 --- a/include/linux/vfio.h +++ b/include/linux/vfio.h @@ -90,4 +90,11 @@ extern void vfio_unregister_iommu_driver( TYPE tmp; \ offsetof(TYPE, MEMBER) + sizeof(tmp.MEMBER); }) \ +/* + * External user API + */ +extern struct vfio_group *vfio_group_get_external_user(struct file *filep); +extern void vfio_group_put_external_user(struct vfio_group *group); +extern int vfio_external_user_iommu_id(struct vfio_group *group); + #endif /* VFIO_H */ -- cgit v1.2.3-71-gd317 From c57acd14ac2d53e40f5c17701c3cc3a092a07b35 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Tue, 23 Jul 2013 01:49:18 +0200 Subject: clk: mux: Add support for read-only muxes. Some platforms have read-only clock muxes that are preconfigured at reset and cannot be changed at runtime. This patch extends mux clock driver to allow handling such read-only muxes by adding new CLK_MUX_READ_ONLY mux flag. Signed-off-by: Tomasz Figa Signed-off-by: Mike Turquette --- drivers/clk/clk-mux.c | 10 +++++++++- include/linux/clk-provider.h | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 614444ca40cd..92f1a1be5319 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -107,6 +107,11 @@ const struct clk_ops clk_mux_ops = { }; EXPORT_SYMBOL_GPL(clk_mux_ops); +const struct clk_ops clk_mux_ro_ops = { + .get_parent = clk_mux_get_parent, +}; +EXPORT_SYMBOL_GPL(clk_mux_ro_ops); + struct clk *clk_register_mux_table(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u32 mask, @@ -133,7 +138,10 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, } init.name = name; - init.ops = &clk_mux_ops; + if (clk_mux_flags & CLK_MUX_READ_ONLY) + init.ops = &clk_mux_ro_ops; + else + init.ops = &clk_mux_ops; init.flags = flags | CLK_IS_BASIC; init.parent_names = parent_names; init.num_parents = num_parents; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 1ec14a732176..9487b96939e8 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -327,8 +327,10 @@ struct clk_mux { #define CLK_MUX_INDEX_ONE BIT(0) #define CLK_MUX_INDEX_BIT BIT(1) #define CLK_MUX_HIWORD_MASK BIT(2) +#define CLK_MUX_READ_ONLY BIT(3) /* mux setting cannot be changed */ extern const struct clk_ops clk_mux_ops; +extern const struct clk_ops clk_mux_ro_ops; struct clk *clk_register_mux(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, -- cgit v1.2.3-71-gd317 From 06dda9d770063c5fefc0b41c7bfcc1321e2649dd Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 24 Jul 2013 01:55:15 +0200 Subject: clk: samsung: Add clock driver for S3C64xx SoCs This patch adds new, Common Clock Framework-based clock driver for Samsung S3C64xx SoCs. The driver is just added, without actually letting the platforms use it yet, since this requires more intermediate steps. Signed-off-by: Tomasz Figa Signed-off-by: Mike Turquette --- .../bindings/clock/samsung,s3c64xx-clock.txt | 77 ++++ drivers/clk/samsung/Makefile | 3 + drivers/clk/samsung/clk-s3c64xx.c | 473 +++++++++++++++++++++ include/dt-bindings/clock/samsung,s3c64xx-clock.h | 178 ++++++++ 4 files changed, 731 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/samsung,s3c64xx-clock.txt create mode 100644 drivers/clk/samsung/clk-s3c64xx.c create mode 100644 include/dt-bindings/clock/samsung,s3c64xx-clock.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/samsung,s3c64xx-clock.txt b/Documentation/devicetree/bindings/clock/samsung,s3c64xx-clock.txt new file mode 100644 index 000000000000..fa171dc4bd3c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/samsung,s3c64xx-clock.txt @@ -0,0 +1,77 @@ +* Samsung S3C64xx Clock Controller + +The S3C64xx clock controller generates and supplies clock to various controllers +within the SoC. The clock binding described here is applicable to all SoCs in +the S3C64xx family. + +Required Properties: + +- compatible: should be one of the following. + - "samsung,s3c6400-clock" - controller compatible with S3C6400 SoC. + - "samsung,s3c6410-clock" - controller compatible with S3C6410 SoC. + +- reg: physical base address of the controller and length of memory mapped + region. + +- #clock-cells: should be 1. + +Each clock is assigned an identifier and client nodes can use this identifier +to specify the clock which they consume. Some of the clocks are available only +on a particular S3C64xx SoC and this is specified where applicable. + +All available clocks are defined as preprocessor macros in +dt-bindings/clock/samsung,s3c64xx-clock.h header and can be used in device +tree sources. + +External clocks: + +There are several clocks that are generated outside the SoC. It is expected +that they are defined using standard clock bindings with following +clock-output-names: + - "fin_pll" - PLL input clock (xtal/extclk) - required, + - "xusbxti" - USB xtal - required, + - "iiscdclk0" - I2S0 codec clock - optional, + - "iiscdclk1" - I2S1 codec clock - optional, + - "iiscdclk2" - I2S2 codec clock - optional, + - "pcmcdclk0" - PCM0 codec clock - optional, + - "pcmcdclk1" - PCM1 codec clock - optional, only S3C6410. + +Example: Clock controller node: + + clock: clock-controller@7e00f000 { + compatible = "samsung,s3c6410-clock"; + reg = <0x7e00f000 0x1000>; + #clock-cells = <1>; + }; + +Example: Required external clocks: + + fin_pll: clock-fin-pll { + compatible = "fixed-clock"; + clock-output-names = "fin_pll"; + clock-frequency = <12000000>; + #clock-cells = <0>; + }; + + xusbxti: clock-xusbxti { + compatible = "fixed-clock"; + clock-output-names = "xusbxti"; + clock-frequency = <48000000>; + #clock-cells = <0>; + }; + +Example: UART controller node that consumes the clock generated by the clock + controller (refer to the standard clock bindings for information about + "clocks" and "clock-names" properties): + + uart0: serial@7f005000 { + compatible = "samsung,s3c6400-uart"; + reg = <0x7f005000 0x100>; + interrupt-parent = <&vic1>; + interrupts = <5>; + clock-names = "uart", "clk_uart_baud2", + "clk_uart_baud3"; + clocks = <&clock PCLK_UART0>, <&clocks PCLK_UART0>, + <&clock SCLK_UART>; + status = "disabled"; + }; diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index 5d4d432cc4ac..3413380086d5 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -8,3 +8,6 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o +ifdef CONFIG_COMMON_CLK +obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o +endif diff --git a/drivers/clk/samsung/clk-s3c64xx.c b/drivers/clk/samsung/clk-s3c64xx.c new file mode 100644 index 000000000000..eeda5675bd17 --- /dev/null +++ b/drivers/clk/samsung/clk-s3c64xx.c @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2013 Tomasz Figa + * + * 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. + * + * Common Clock Framework support for all S3C64xx SoCs. +*/ + +#include +#include +#include +#include +#include + +#include + +#include "clk.h" +#include "clk-pll.h" + +/* S3C64xx clock controller register offsets. */ +#define APLL_LOCK 0x000 +#define MPLL_LOCK 0x004 +#define EPLL_LOCK 0x008 +#define APLL_CON 0x00c +#define MPLL_CON 0x010 +#define EPLL_CON0 0x014 +#define EPLL_CON1 0x018 +#define CLK_SRC 0x01c +#define CLK_DIV0 0x020 +#define CLK_DIV1 0x024 +#define CLK_DIV2 0x028 +#define HCLK_GATE 0x030 +#define PCLK_GATE 0x034 +#define SCLK_GATE 0x038 +#define MEM0_GATE 0x03c +#define CLK_SRC2 0x10c +#define OTHERS 0x900 + +/* Helper macros to define clock arrays. */ +#define FIXED_RATE_CLOCKS(name) \ + static struct samsung_fixed_rate_clock name[] +#define MUX_CLOCKS(name) \ + static struct samsung_mux_clock name[] +#define DIV_CLOCKS(name) \ + static struct samsung_div_clock name[] +#define GATE_CLOCKS(name) \ + static struct samsung_gate_clock name[] + +/* Helper macros for gate types present on S3C64xx. */ +#define GATE_BUS(_id, cname, pname, o, b) \ + GATE(_id, cname, pname, o, b, 0, 0) +#define GATE_SCLK(_id, cname, pname, o, b) \ + GATE(_id, cname, pname, o, b, CLK_SET_RATE_PARENT, 0) +#define GATE_ON(_id, cname, pname, o, b) \ + GATE(_id, cname, pname, o, b, CLK_IGNORE_UNUSED, 0) + +/* list of PLLs to be registered */ +enum s3c64xx_plls { + apll, mpll, epll, +}; + +/* + * List of controller registers to be saved and restored during + * a suspend/resume cycle. + */ +static __initdata unsigned long s3c64xx_clk_regs[] = { + APLL_LOCK, + MPLL_LOCK, + EPLL_LOCK, + APLL_CON, + MPLL_CON, + EPLL_CON0, + EPLL_CON1, + CLK_SRC, + CLK_DIV0, + CLK_DIV1, + CLK_DIV2, + HCLK_GATE, + PCLK_GATE, + SCLK_GATE, +}; + +static __initdata unsigned long s3c6410_clk_regs[] = { + CLK_SRC2, + MEM0_GATE, +}; + +/* List of parent clocks common for all S3C64xx SoCs. */ +PNAME(spi_mmc_p) = { "mout_epll", "dout_mpll", "fin_pll", "clk27m" }; +PNAME(uart_p) = { "mout_epll", "dout_mpll" }; +PNAME(audio0_p) = { "mout_epll", "dout_mpll", "fin_pll", "iiscdclk0", + "pcmcdclk0", "none", "none", "none" }; +PNAME(audio1_p) = { "mout_epll", "dout_mpll", "fin_pll", "iiscdclk1", + "pcmcdclk0", "none", "none", "none" }; +PNAME(mfc_p) = { "hclkx2", "mout_epll" }; +PNAME(apll_p) = { "fin_pll", "fout_apll" }; +PNAME(mpll_p) = { "fin_pll", "fout_mpll" }; +PNAME(epll_p) = { "fin_pll", "fout_epll" }; +PNAME(hclkx2_p) = { "mout_mpll", "mout_apll" }; + +/* S3C6400-specific parent clocks. */ +PNAME(scaler_lcd_p6400) = { "mout_epll", "dout_mpll", "none", "none" }; +PNAME(irda_p6400) = { "mout_epll", "dout_mpll", "none", "clk48m" }; +PNAME(uhost_p6400) = { "clk48m", "mout_epll", "dout_mpll", "none" }; + +/* S3C6410-specific parent clocks. */ +PNAME(clk27_p6410) = { "clk27m", "fin_pll" }; +PNAME(scaler_lcd_p6410) = { "mout_epll", "dout_mpll", "fin_pll", "none" }; +PNAME(irda_p6410) = { "mout_epll", "dout_mpll", "fin_pll", "clk48m" }; +PNAME(uhost_p6410) = { "clk48m", "mout_epll", "dout_mpll", "fin_pll" }; +PNAME(audio2_p6410) = { "mout_epll", "dout_mpll", "fin_pll", "iiscdclk2", + "pcmcdclk1", "none", "none", "none" }; + +/* Fixed rate clocks generated outside the SoC. */ +FIXED_RATE_CLOCKS(s3c64xx_fixed_rate_ext_clks) __initdata = { + FRATE(0, "fin_pll", NULL, CLK_IS_ROOT, 0), + FRATE(0, "xusbxti", NULL, CLK_IS_ROOT, 0), +}; + +/* Fixed rate clocks generated inside the SoC. */ +FIXED_RATE_CLOCKS(s3c64xx_fixed_rate_clks) __initdata = { + FRATE(CLK27M, "clk27m", NULL, CLK_IS_ROOT, 27000000), + FRATE(CLK48M, "clk48m", NULL, CLK_IS_ROOT, 48000000), +}; + +/* List of clock muxes present on all S3C64xx SoCs. */ +MUX_CLOCKS(s3c64xx_mux_clks) __initdata = { + MUX_F(0, "mout_syncmux", hclkx2_p, OTHERS, 6, 1, 0, CLK_MUX_READ_ONLY), + MUX(MOUT_APLL, "mout_apll", apll_p, CLK_SRC, 0, 1), + MUX(MOUT_MPLL, "mout_mpll", mpll_p, CLK_SRC, 1, 1), + MUX(MOUT_EPLL, "mout_epll", epll_p, CLK_SRC, 2, 1), + MUX(MOUT_MFC, "mout_mfc", mfc_p, CLK_SRC, 4, 1), + MUX(MOUT_AUDIO0, "mout_audio0", audio0_p, CLK_SRC, 7, 3), + MUX(MOUT_AUDIO1, "mout_audio1", audio1_p, CLK_SRC, 10, 3), + MUX(MOUT_UART, "mout_uart", uart_p, CLK_SRC, 13, 1), + MUX(MOUT_SPI0, "mout_spi0", spi_mmc_p, CLK_SRC, 14, 2), + MUX(MOUT_SPI1, "mout_spi1", spi_mmc_p, CLK_SRC, 16, 2), + MUX(MOUT_MMC0, "mout_mmc0", spi_mmc_p, CLK_SRC, 18, 2), + MUX(MOUT_MMC1, "mout_mmc1", spi_mmc_p, CLK_SRC, 20, 2), + MUX(MOUT_MMC2, "mout_mmc2", spi_mmc_p, CLK_SRC, 22, 2), +}; + +/* List of clock muxes present on S3C6400. */ +MUX_CLOCKS(s3c6400_mux_clks) __initdata = { + MUX(MOUT_UHOST, "mout_uhost", uhost_p6400, CLK_SRC, 5, 2), + MUX(MOUT_IRDA, "mout_irda", irda_p6400, CLK_SRC, 24, 2), + MUX(MOUT_LCD, "mout_lcd", scaler_lcd_p6400, CLK_SRC, 26, 2), + MUX(MOUT_SCALER, "mout_scaler", scaler_lcd_p6400, CLK_SRC, 28, 2), +}; + +/* List of clock muxes present on S3C6410. */ +MUX_CLOCKS(s3c6410_mux_clks) __initdata = { + MUX(MOUT_UHOST, "mout_uhost", uhost_p6410, CLK_SRC, 5, 2), + MUX(MOUT_IRDA, "mout_irda", irda_p6410, CLK_SRC, 24, 2), + MUX(MOUT_LCD, "mout_lcd", scaler_lcd_p6410, CLK_SRC, 26, 2), + MUX(MOUT_SCALER, "mout_scaler", scaler_lcd_p6410, CLK_SRC, 28, 2), + MUX(MOUT_DAC27, "mout_dac27", clk27_p6410, CLK_SRC, 30, 1), + MUX(MOUT_TV27, "mout_tv27", clk27_p6410, CLK_SRC, 31, 1), + MUX(MOUT_AUDIO2, "mout_audio2", audio2_p6410, CLK_SRC2, 0, 3), +}; + +/* List of clock dividers present on all S3C64xx SoCs. */ +DIV_CLOCKS(s3c64xx_div_clks) __initdata = { + DIV(DOUT_MPLL, "dout_mpll", "mout_mpll", CLK_DIV0, 4, 1), + DIV(HCLKX2, "hclkx2", "mout_syncmux", CLK_DIV0, 9, 3), + DIV(HCLK, "hclk", "hclkx2", CLK_DIV0, 8, 1), + DIV(PCLK, "pclk", "hclkx2", CLK_DIV0, 12, 4), + DIV(DOUT_SECUR, "dout_secur", "hclkx2", CLK_DIV0, 18, 2), + DIV(DOUT_CAM, "dout_cam", "hclkx2", CLK_DIV0, 20, 4), + DIV(DOUT_JPEG, "dout_jpeg", "hclkx2", CLK_DIV0, 24, 4), + DIV(DOUT_MFC, "dout_mfc", "mout_mfc", CLK_DIV0, 28, 4), + DIV(DOUT_MMC0, "dout_mmc0", "mout_mmc0", CLK_DIV1, 0, 4), + DIV(DOUT_MMC1, "dout_mmc1", "mout_mmc1", CLK_DIV1, 4, 4), + DIV(DOUT_MMC2, "dout_mmc2", "mout_mmc2", CLK_DIV1, 8, 4), + DIV(DOUT_LCD, "dout_lcd", "mout_lcd", CLK_DIV1, 12, 4), + DIV(DOUT_SCALER, "dout_scaler", "mout_scaler", CLK_DIV1, 16, 4), + DIV(DOUT_UHOST, "dout_uhost", "mout_uhost", CLK_DIV1, 20, 4), + DIV(DOUT_SPI0, "dout_spi0", "mout_spi0", CLK_DIV2, 0, 4), + DIV(DOUT_SPI1, "dout_spi1", "mout_spi1", CLK_DIV2, 4, 4), + DIV(DOUT_AUDIO0, "dout_audio0", "mout_audio0", CLK_DIV2, 8, 4), + DIV(DOUT_AUDIO1, "dout_audio1", "mout_audio1", CLK_DIV2, 12, 4), + DIV(DOUT_UART, "dout_uart", "mout_uart", CLK_DIV2, 16, 4), + DIV(DOUT_IRDA, "dout_irda", "mout_irda", CLK_DIV2, 20, 4), +}; + +/* List of clock dividers present on S3C6400. */ +DIV_CLOCKS(s3c6400_div_clks) __initdata = { + DIV(ARMCLK, "armclk", "mout_apll", CLK_DIV0, 0, 3), +}; + +/* List of clock dividers present on S3C6410. */ +DIV_CLOCKS(s3c6410_div_clks) __initdata = { + DIV(ARMCLK, "armclk", "mout_apll", CLK_DIV0, 0, 4), + DIV(DOUT_FIMC, "dout_fimc", "hclk", CLK_DIV1, 24, 4), + DIV(DOUT_AUDIO2, "dout_audio2", "mout_audio2", CLK_DIV2, 24, 4), +}; + +/* List of clock gates present on all S3C64xx SoCs. */ +GATE_CLOCKS(s3c64xx_gate_clks) __initdata = { + GATE_BUS(HCLK_UHOST, "hclk_uhost", "hclk", HCLK_GATE, 29), + GATE_BUS(HCLK_SECUR, "hclk_secur", "hclk", HCLK_GATE, 28), + GATE_BUS(HCLK_SDMA1, "hclk_sdma1", "hclk", HCLK_GATE, 27), + GATE_BUS(HCLK_SDMA0, "hclk_sdma0", "hclk", HCLK_GATE, 26), + GATE_ON(HCLK_DDR1, "hclk_ddr1", "hclk", HCLK_GATE, 24), + GATE_BUS(HCLK_USB, "hclk_usb", "hclk", HCLK_GATE, 20), + GATE_BUS(HCLK_HSMMC2, "hclk_hsmmc2", "hclk", HCLK_GATE, 19), + GATE_BUS(HCLK_HSMMC1, "hclk_hsmmc1", "hclk", HCLK_GATE, 18), + GATE_BUS(HCLK_HSMMC0, "hclk_hsmmc0", "hclk", HCLK_GATE, 17), + GATE_BUS(HCLK_MDP, "hclk_mdp", "hclk", HCLK_GATE, 16), + GATE_BUS(HCLK_DHOST, "hclk_dhost", "hclk", HCLK_GATE, 15), + GATE_BUS(HCLK_IHOST, "hclk_ihost", "hclk", HCLK_GATE, 14), + GATE_BUS(HCLK_DMA1, "hclk_dma1", "hclk", HCLK_GATE, 13), + GATE_BUS(HCLK_DMA0, "hclk_dma0", "hclk", HCLK_GATE, 12), + GATE_BUS(HCLK_JPEG, "hclk_jpeg", "hclk", HCLK_GATE, 11), + GATE_BUS(HCLK_CAMIF, "hclk_camif", "hclk", HCLK_GATE, 10), + GATE_BUS(HCLK_SCALER, "hclk_scaler", "hclk", HCLK_GATE, 9), + GATE_BUS(HCLK_2D, "hclk_2d", "hclk", HCLK_GATE, 8), + GATE_BUS(HCLK_TV, "hclk_tv", "hclk", HCLK_GATE, 7), + GATE_BUS(HCLK_POST0, "hclk_post0", "hclk", HCLK_GATE, 5), + GATE_BUS(HCLK_ROT, "hclk_rot", "hclk", HCLK_GATE, 4), + GATE_BUS(HCLK_LCD, "hclk_lcd", "hclk", HCLK_GATE, 3), + GATE_BUS(HCLK_TZIC, "hclk_tzic", "hclk", HCLK_GATE, 2), + GATE_ON(HCLK_INTC, "hclk_intc", "hclk", HCLK_GATE, 1), + GATE_ON(PCLK_SKEY, "pclk_skey", "pclk", PCLK_GATE, 24), + GATE_ON(PCLK_CHIPID, "pclk_chipid", "pclk", PCLK_GATE, 23), + GATE_BUS(PCLK_SPI1, "pclk_spi1", "pclk", PCLK_GATE, 22), + GATE_BUS(PCLK_SPI0, "pclk_spi0", "pclk", PCLK_GATE, 21), + GATE_BUS(PCLK_HSIRX, "pclk_hsirx", "pclk", PCLK_GATE, 20), + GATE_BUS(PCLK_HSITX, "pclk_hsitx", "pclk", PCLK_GATE, 19), + GATE_ON(PCLK_GPIO, "pclk_gpio", "pclk", PCLK_GATE, 18), + GATE_BUS(PCLK_IIC0, "pclk_iic0", "pclk", PCLK_GATE, 17), + GATE_BUS(PCLK_IIS1, "pclk_iis1", "pclk", PCLK_GATE, 16), + GATE_BUS(PCLK_IIS0, "pclk_iis0", "pclk", PCLK_GATE, 15), + GATE_BUS(PCLK_AC97, "pclk_ac97", "pclk", PCLK_GATE, 14), + GATE_BUS(PCLK_TZPC, "pclk_tzpc", "pclk", PCLK_GATE, 13), + GATE_BUS(PCLK_TSADC, "pclk_tsadc", "pclk", PCLK_GATE, 12), + GATE_BUS(PCLK_KEYPAD, "pclk_keypad", "pclk", PCLK_GATE, 11), + GATE_BUS(PCLK_IRDA, "pclk_irda", "pclk", PCLK_GATE, 10), + GATE_BUS(PCLK_PCM1, "pclk_pcm1", "pclk", PCLK_GATE, 9), + GATE_BUS(PCLK_PCM0, "pclk_pcm0", "pclk", PCLK_GATE, 8), + GATE_BUS(PCLK_PWM, "pclk_pwm", "pclk", PCLK_GATE, 7), + GATE_BUS(PCLK_RTC, "pclk_rtc", "pclk", PCLK_GATE, 6), + GATE_BUS(PCLK_WDT, "pclk_wdt", "pclk", PCLK_GATE, 5), + GATE_BUS(PCLK_UART3, "pclk_uart3", "pclk", PCLK_GATE, 4), + GATE_BUS(PCLK_UART2, "pclk_uart2", "pclk", PCLK_GATE, 3), + GATE_BUS(PCLK_UART1, "pclk_uart1", "pclk", PCLK_GATE, 2), + GATE_BUS(PCLK_UART0, "pclk_uart0", "pclk", PCLK_GATE, 1), + GATE_BUS(PCLK_MFC, "pclk_mfc", "pclk", PCLK_GATE, 0), + GATE_SCLK(SCLK_UHOST, "sclk_uhost", "dout_uhost", SCLK_GATE, 30), + GATE_SCLK(SCLK_MMC2_48, "sclk_mmc2_48", "clk48m", SCLK_GATE, 29), + GATE_SCLK(SCLK_MMC1_48, "sclk_mmc1_48", "clk48m", SCLK_GATE, 28), + GATE_SCLK(SCLK_MMC0_48, "sclk_mmc0_48", "clk48m", SCLK_GATE, 27), + GATE_SCLK(SCLK_MMC2, "sclk_mmc2", "dout_mmc2", SCLK_GATE, 26), + GATE_SCLK(SCLK_MMC1, "sclk_mmc1", "dout_mmc1", SCLK_GATE, 25), + GATE_SCLK(SCLK_MMC0, "sclk_mmc0", "dout_mmc0", SCLK_GATE, 24), + GATE_SCLK(SCLK_SPI1_48, "sclk_spi1_48", "clk48m", SCLK_GATE, 23), + GATE_SCLK(SCLK_SPI0_48, "sclk_spi0_48", "clk48m", SCLK_GATE, 22), + GATE_SCLK(SCLK_SPI1, "sclk_spi1", "dout_spi1", SCLK_GATE, 21), + GATE_SCLK(SCLK_SPI0, "sclk_spi0", "dout_spi0", SCLK_GATE, 20), + GATE_SCLK(SCLK_DAC27, "sclk_dac27", "mout_dac27", SCLK_GATE, 19), + GATE_SCLK(SCLK_TV27, "sclk_tv27", "mout_tv27", SCLK_GATE, 18), + GATE_SCLK(SCLK_SCALER27, "sclk_scaler27", "clk27m", SCLK_GATE, 17), + GATE_SCLK(SCLK_SCALER, "sclk_scaler", "dout_scaler", SCLK_GATE, 16), + GATE_SCLK(SCLK_LCD27, "sclk_lcd27", "clk27m", SCLK_GATE, 15), + GATE_SCLK(SCLK_LCD, "sclk_lcd", "dout_lcd", SCLK_GATE, 14), + GATE_SCLK(SCLK_POST0_27, "sclk_post0_27", "clk27m", SCLK_GATE, 12), + GATE_SCLK(SCLK_POST0, "sclk_post0", "dout_lcd", SCLK_GATE, 10), + GATE_SCLK(SCLK_AUDIO1, "sclk_audio1", "dout_audio1", SCLK_GATE, 9), + GATE_SCLK(SCLK_AUDIO0, "sclk_audio0", "dout_audio0", SCLK_GATE, 8), + GATE_SCLK(SCLK_SECUR, "sclk_secur", "dout_secur", SCLK_GATE, 7), + GATE_SCLK(SCLK_IRDA, "sclk_irda", "dout_irda", SCLK_GATE, 6), + GATE_SCLK(SCLK_UART, "sclk_uart", "dout_uart", SCLK_GATE, 5), + GATE_SCLK(SCLK_MFC, "sclk_mfc", "dout_mfc", SCLK_GATE, 3), + GATE_SCLK(SCLK_CAM, "sclk_cam", "dout_cam", SCLK_GATE, 2), + GATE_SCLK(SCLK_JPEG, "sclk_jpeg", "dout_jpeg", SCLK_GATE, 1), +}; + +/* List of clock gates present on S3C6400. */ +GATE_CLOCKS(s3c6400_gate_clks) __initdata = { + GATE_ON(HCLK_DDR0, "hclk_ddr0", "hclk", HCLK_GATE, 23), + GATE_SCLK(SCLK_ONENAND, "sclk_onenand", "parent", SCLK_GATE, 4), +}; + +/* List of clock gates present on S3C6410. */ +GATE_CLOCKS(s3c6410_gate_clks) __initdata = { + GATE_BUS(HCLK_3DSE, "hclk_3dse", "hclk", HCLK_GATE, 31), + GATE_ON(HCLK_IROM, "hclk_irom", "hclk", HCLK_GATE, 25), + GATE_ON(HCLK_MEM1, "hclk_mem1", "hclk", HCLK_GATE, 22), + GATE_ON(HCLK_MEM0, "hclk_mem0", "hclk", HCLK_GATE, 21), + GATE_BUS(HCLK_MFC, "hclk_mfc", "hclk", HCLK_GATE, 0), + GATE_BUS(PCLK_IIC1, "pclk_iic1", "pclk", PCLK_GATE, 27), + GATE_BUS(PCLK_IIS2, "pclk_iis2", "pclk", PCLK_GATE, 26), + GATE_SCLK(SCLK_FIMC, "sclk_fimc", "dout_fimc", SCLK_GATE, 13), + GATE_SCLK(SCLK_AUDIO2, "sclk_audio2", "dout_audio2", SCLK_GATE, 11), + GATE_BUS(MEM0_CFCON, "mem0_cfcon", "hclk_mem0", MEM0_GATE, 5), + GATE_BUS(MEM0_ONENAND1, "mem0_onenand1", "hclk_mem0", MEM0_GATE, 4), + GATE_BUS(MEM0_ONENAND0, "mem0_onenand0", "hclk_mem0", MEM0_GATE, 3), + GATE_BUS(MEM0_NFCON, "mem0_nfcon", "hclk_mem0", MEM0_GATE, 2), + GATE_ON(MEM0_SROM, "mem0_srom", "hclk_mem0", MEM0_GATE, 1), +}; + +/* List of PLL clocks. */ +static struct samsung_pll_clock s3c64xx_pll_clks[] __initdata = { + [apll] = PLL(pll_6552, FOUT_APLL, "fout_apll", "fin_pll", + APLL_LOCK, APLL_CON, NULL), + [mpll] = PLL(pll_6552, FOUT_MPLL, "fout_mpll", "fin_pll", + MPLL_LOCK, MPLL_CON, NULL), + [epll] = PLL(pll_6553, FOUT_EPLL, "fout_epll", "fin_pll", + EPLL_LOCK, EPLL_CON0, NULL), +}; + +/* Aliases for common s3c64xx clocks. */ +static struct samsung_clock_alias s3c64xx_clock_aliases[] = { + ALIAS(FOUT_APLL, NULL, "fout_apll"), + ALIAS(FOUT_MPLL, NULL, "fout_mpll"), + ALIAS(FOUT_EPLL, NULL, "fout_epll"), + ALIAS(MOUT_EPLL, NULL, "mout_epll"), + ALIAS(DOUT_MPLL, NULL, "dout_mpll"), + ALIAS(HCLKX2, NULL, "hclk2"), + ALIAS(HCLK, NULL, "hclk"), + ALIAS(PCLK, NULL, "pclk"), + ALIAS(PCLK, NULL, "clk_uart_baud2"), + ALIAS(ARMCLK, NULL, "armclk"), + ALIAS(HCLK_UHOST, "s3c2410-ohci", "usb-host"), + ALIAS(HCLK_USB, "s3c-hsotg", "otg"), + ALIAS(HCLK_HSMMC2, "s3c-sdhci.2", "hsmmc"), + ALIAS(HCLK_HSMMC2, "s3c-sdhci.2", "mmc_busclk.0"), + ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "hsmmc"), + ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.0"), + ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "hsmmc"), + ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "mmc_busclk.0"), + ALIAS(HCLK_DMA1, NULL, "dma1"), + ALIAS(HCLK_DMA0, NULL, "dma0"), + ALIAS(HCLK_CAMIF, "s3c-camif", "camif"), + ALIAS(HCLK_LCD, "s3c-fb", "lcd"), + ALIAS(PCLK_SPI1, "s3c6410-spi.1", "spi"), + ALIAS(PCLK_SPI0, "s3c6410-spi.0", "spi"), + ALIAS(PCLK_IIC0, "s3c2440-i2c.0", "i2c"), + ALIAS(PCLK_IIS1, "samsung-i2s.1", "iis"), + ALIAS(PCLK_IIS0, "samsung-i2s.0", "iis"), + ALIAS(PCLK_AC97, "samsung-ac97", "ac97"), + ALIAS(PCLK_TSADC, "s3c64xx-adc", "adc"), + ALIAS(PCLK_KEYPAD, "samsung-keypad", "keypad"), + ALIAS(PCLK_PCM1, "samsung-pcm.1", "pcm"), + ALIAS(PCLK_PCM0, "samsung-pcm.0", "pcm"), + ALIAS(PCLK_PWM, NULL, "timers"), + ALIAS(PCLK_RTC, "s3c64xx-rtc", "rtc"), + ALIAS(PCLK_WDT, NULL, "watchdog"), + ALIAS(PCLK_UART3, "s3c6400-uart.3", "uart"), + ALIAS(PCLK_UART2, "s3c6400-uart.2", "uart"), + ALIAS(PCLK_UART1, "s3c6400-uart.1", "uart"), + ALIAS(PCLK_UART0, "s3c6400-uart.0", "uart"), + ALIAS(SCLK_UHOST, "s3c2410-ohci", "usb-bus-host"), + ALIAS(SCLK_MMC2, "s3c-sdhci.2", "mmc_busclk.2"), + ALIAS(SCLK_MMC1, "s3c-sdhci.1", "mmc_busclk.2"), + ALIAS(SCLK_MMC0, "s3c-sdhci.0", "mmc_busclk.2"), + ALIAS(SCLK_SPI1, "s3c6410-spi.1", "spi-bus"), + ALIAS(SCLK_SPI0, "s3c6410-spi.0", "spi-bus"), + ALIAS(SCLK_AUDIO1, "samsung-pcm.1", "audio-bus"), + ALIAS(SCLK_AUDIO1, "samsung-i2s.1", "audio-bus"), + ALIAS(SCLK_AUDIO0, "samsung-pcm.0", "audio-bus"), + ALIAS(SCLK_AUDIO0, "samsung-i2s.0", "audio-bus"), + ALIAS(SCLK_UART, NULL, "clk_uart_baud3"), + ALIAS(SCLK_CAM, "s3c-camif", "camera"), +}; + +/* Aliases for s3c6400-specific clocks. */ +static struct samsung_clock_alias s3c6400_clock_aliases[] = { + /* Nothing to place here yet. */ +}; + +/* Aliases for s3c6410-specific clocks. */ +static struct samsung_clock_alias s3c6410_clock_aliases[] = { + ALIAS(PCLK_IIC1, "s3c2440-i2c.1", "i2c"), + ALIAS(PCLK_IIS2, "samsung-i2s.2", "iis"), + ALIAS(SCLK_FIMC, "s3c-camif", "fimc"), + ALIAS(SCLK_AUDIO2, "samsung-i2s.2", "audio-bus"), + ALIAS(MEM0_SROM, NULL, "srom"), +}; + +static void __init s3c64xx_clk_register_fixed_ext(unsigned long fin_pll_f, + unsigned long xusbxti_f) +{ + s3c64xx_fixed_rate_ext_clks[0].fixed_rate = fin_pll_f; + s3c64xx_fixed_rate_ext_clks[1].fixed_rate = xusbxti_f; + samsung_clk_register_fixed_rate(s3c64xx_fixed_rate_ext_clks, + ARRAY_SIZE(s3c64xx_fixed_rate_ext_clks)); +} + +/* Register s3c64xx clocks. */ +void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f, + unsigned long xusbxti_f, bool is_s3c6400, + void __iomem *reg_base) +{ + unsigned long *soc_regs = NULL; + unsigned long nr_soc_regs = 0; + + if (np) { + reg_base = of_iomap(np, 0); + if (!reg_base) + panic("%s: failed to map registers\n", __func__); + } + + if (!is_s3c6400) { + soc_regs = s3c6410_clk_regs; + nr_soc_regs = ARRAY_SIZE(s3c6410_clk_regs); + } + + samsung_clk_init(np, reg_base, NR_CLKS, s3c64xx_clk_regs, + ARRAY_SIZE(s3c64xx_clk_regs), soc_regs, nr_soc_regs); + + /* Register external clocks. */ + if (!np) + s3c64xx_clk_register_fixed_ext(xtal_f, xusbxti_f); + + /* Register PLLs. */ + samsung_clk_register_pll(s3c64xx_pll_clks, + ARRAY_SIZE(s3c64xx_pll_clks), reg_base); + + /* Register common internal clocks. */ + samsung_clk_register_fixed_rate(s3c64xx_fixed_rate_clks, + ARRAY_SIZE(s3c64xx_fixed_rate_clks)); + samsung_clk_register_mux(s3c64xx_mux_clks, + ARRAY_SIZE(s3c64xx_mux_clks)); + samsung_clk_register_div(s3c64xx_div_clks, + ARRAY_SIZE(s3c64xx_div_clks)); + samsung_clk_register_gate(s3c64xx_gate_clks, + ARRAY_SIZE(s3c64xx_gate_clks)); + + /* Register SoC-specific clocks. */ + if (is_s3c6400) { + samsung_clk_register_mux(s3c6400_mux_clks, + ARRAY_SIZE(s3c6400_mux_clks)); + samsung_clk_register_div(s3c6400_div_clks, + ARRAY_SIZE(s3c6400_div_clks)); + samsung_clk_register_gate(s3c6400_gate_clks, + ARRAY_SIZE(s3c6400_gate_clks)); + samsung_clk_register_alias(s3c6400_clock_aliases, + ARRAY_SIZE(s3c6400_clock_aliases)); + } else { + samsung_clk_register_mux(s3c6410_mux_clks, + ARRAY_SIZE(s3c6410_mux_clks)); + samsung_clk_register_div(s3c6410_div_clks, + ARRAY_SIZE(s3c6410_div_clks)); + samsung_clk_register_gate(s3c6410_gate_clks, + ARRAY_SIZE(s3c6410_gate_clks)); + samsung_clk_register_alias(s3c6410_clock_aliases, + ARRAY_SIZE(s3c6410_clock_aliases)); + } + + samsung_clk_register_alias(s3c64xx_clock_aliases, + ARRAY_SIZE(s3c64xx_clock_aliases)); + + pr_info("%s clocks: apll = %lu, mpll = %lu\n" + "\tepll = %lu, arm_clk = %lu\n", + is_s3c6400 ? "S3C6400" : "S3C6410", + _get_rate("fout_apll"), _get_rate("fout_mpll"), + _get_rate("fout_epll"), _get_rate("armclk")); +} + +static void __init s3c6400_clk_init(struct device_node *np) +{ + s3c64xx_clk_init(np, 0, 0, true, NULL); +} +CLK_OF_DECLARE(s3c6400_clk, "samsung,s3c6400-clock", s3c6400_clk_init); + +static void __init s3c6410_clk_init(struct device_node *np) +{ + s3c64xx_clk_init(np, 0, 0, false, NULL); +} +CLK_OF_DECLARE(s3c6410_clk, "samsung,s3c6410-clock", s3c6410_clk_init); diff --git a/include/dt-bindings/clock/samsung,s3c64xx-clock.h b/include/dt-bindings/clock/samsung,s3c64xx-clock.h new file mode 100644 index 000000000000..ad95c7f50090 --- /dev/null +++ b/include/dt-bindings/clock/samsung,s3c64xx-clock.h @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2013 Tomasz Figa + * + * 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. + * + * Device Tree binding constants for Samsung S3C64xx clock controller. +*/ + +#ifndef _DT_BINDINGS_CLOCK_SAMSUNG_S3C64XX_CLOCK_H +#define _DT_BINDINGS_CLOCK_SAMSUNG_S3C64XX_CLOCK_H + +/* + * Let each exported clock get a unique index, which is used on DT-enabled + * platforms to lookup the clock from a clock specifier. These indices are + * therefore considered an ABI and so must not be changed. This implies + * that new clocks should be added either in free spaces between clock groups + * or at the end. + */ + +/* Core clocks. */ +#define CLK27M 1 +#define CLK48M 2 +#define FOUT_APLL 3 +#define FOUT_MPLL 4 +#define FOUT_EPLL 5 +#define ARMCLK 6 +#define HCLKX2 7 +#define HCLK 8 +#define PCLK 9 + +/* HCLK bus clocks. */ +#define HCLK_3DSE 16 +#define HCLK_UHOST 17 +#define HCLK_SECUR 18 +#define HCLK_SDMA1 19 +#define HCLK_SDMA0 20 +#define HCLK_IROM 21 +#define HCLK_DDR1 22 +#define HCLK_MEM1 23 +#define HCLK_MEM0 24 +#define HCLK_USB 25 +#define HCLK_HSMMC2 26 +#define HCLK_HSMMC1 27 +#define HCLK_HSMMC0 28 +#define HCLK_MDP 29 +#define HCLK_DHOST 30 +#define HCLK_IHOST 31 +#define HCLK_DMA1 32 +#define HCLK_DMA0 33 +#define HCLK_JPEG 34 +#define HCLK_CAMIF 35 +#define HCLK_SCALER 36 +#define HCLK_2D 37 +#define HCLK_TV 38 +#define HCLK_POST0 39 +#define HCLK_ROT 40 +#define HCLK_LCD 41 +#define HCLK_TZIC 42 +#define HCLK_INTC 43 +#define HCLK_MFC 44 +#define HCLK_DDR0 45 + +/* PCLK bus clocks. */ +#define PCLK_IIC1 48 +#define PCLK_IIS2 49 +#define PCLK_SKEY 50 +#define PCLK_CHIPID 51 +#define PCLK_SPI1 52 +#define PCLK_SPI0 53 +#define PCLK_HSIRX 54 +#define PCLK_HSITX 55 +#define PCLK_GPIO 56 +#define PCLK_IIC0 57 +#define PCLK_IIS1 58 +#define PCLK_IIS0 59 +#define PCLK_AC97 60 +#define PCLK_TZPC 61 +#define PCLK_TSADC 62 +#define PCLK_KEYPAD 63 +#define PCLK_IRDA 64 +#define PCLK_PCM1 65 +#define PCLK_PCM0 66 +#define PCLK_PWM 67 +#define PCLK_RTC 68 +#define PCLK_WDT 69 +#define PCLK_UART3 70 +#define PCLK_UART2 71 +#define PCLK_UART1 72 +#define PCLK_UART0 73 +#define PCLK_MFC 74 + +/* Special clocks. */ +#define SCLK_UHOST 80 +#define SCLK_MMC2_48 81 +#define SCLK_MMC1_48 82 +#define SCLK_MMC0_48 83 +#define SCLK_MMC2 84 +#define SCLK_MMC1 85 +#define SCLK_MMC0 86 +#define SCLK_SPI1_48 87 +#define SCLK_SPI0_48 88 +#define SCLK_SPI1 89 +#define SCLK_SPI0 90 +#define SCLK_DAC27 91 +#define SCLK_TV27 92 +#define SCLK_SCALER27 93 +#define SCLK_SCALER 94 +#define SCLK_LCD27 95 +#define SCLK_LCD 96 +#define SCLK_FIMC 97 +#define SCLK_POST0_27 98 +#define SCLK_AUDIO2 99 +#define SCLK_POST0 100 +#define SCLK_AUDIO1 101 +#define SCLK_AUDIO0 102 +#define SCLK_SECUR 103 +#define SCLK_IRDA 104 +#define SCLK_UART 105 +#define SCLK_MFC 106 +#define SCLK_CAM 107 +#define SCLK_JPEG 108 +#define SCLK_ONENAND 109 + +/* MEM0 bus clocks - S3C6410-specific. */ +#define MEM0_CFCON 112 +#define MEM0_ONENAND1 113 +#define MEM0_ONENAND0 114 +#define MEM0_NFCON 115 +#define MEM0_SROM 116 + +/* Muxes. */ +#define MOUT_APLL 128 +#define MOUT_MPLL 129 +#define MOUT_EPLL 130 +#define MOUT_MFC 131 +#define MOUT_AUDIO0 132 +#define MOUT_AUDIO1 133 +#define MOUT_UART 134 +#define MOUT_SPI0 135 +#define MOUT_SPI1 136 +#define MOUT_MMC0 137 +#define MOUT_MMC1 138 +#define MOUT_MMC2 139 +#define MOUT_UHOST 140 +#define MOUT_IRDA 141 +#define MOUT_LCD 142 +#define MOUT_SCALER 143 +#define MOUT_DAC27 144 +#define MOUT_TV27 145 +#define MOUT_AUDIO2 146 + +/* Dividers. */ +#define DOUT_MPLL 160 +#define DOUT_SECUR 161 +#define DOUT_CAM 162 +#define DOUT_JPEG 163 +#define DOUT_MFC 164 +#define DOUT_MMC0 165 +#define DOUT_MMC1 166 +#define DOUT_MMC2 167 +#define DOUT_LCD 168 +#define DOUT_SCALER 169 +#define DOUT_UHOST 170 +#define DOUT_SPI0 171 +#define DOUT_SPI1 172 +#define DOUT_AUDIO0 173 +#define DOUT_AUDIO1 174 +#define DOUT_UART 175 +#define DOUT_IRDA 176 +#define DOUT_FIMC 177 +#define DOUT_AUDIO2 178 + +/* Total number of clocks. */ +#define NR_CLKS (DOUT_AUDIO2 + 1) + +#endif /* _DT_BINDINGS_CLOCK_SAMSUNG_S3C64XX_CLOCK_H */ -- cgit v1.2.3-71-gd317 From 5a139296f872d438f17f7219411321b603c1622b Mon Sep 17 00:00:00 2001 From: "fan.du" Date: Mon, 5 Aug 2013 17:13:03 +0800 Subject: sctp: Pack dst_cookie into 1st cacheline hole for 64bit host As dst_cookie is used in fast path sctp_transport_dst_check. Before: struct sctp_transport { struct list_head transports; /* 0 16 */ atomic_t refcnt; /* 16 4 */ __u32 dead:1; /* 20:31 4 */ __u32 rto_pending:1; /* 20:30 4 */ __u32 hb_sent:1; /* 20:29 4 */ __u32 pmtu_pending:1; /* 20:28 4 */ /* XXX 28 bits hole, try to pack */ __u32 sack_generation; /* 24 4 */ /* XXX 4 bytes hole, try to pack */ struct flowi fl; /* 32 64 */ /* --- cacheline 1 boundary (64 bytes) was 32 bytes ago --- */ union sctp_addr ipaddr; /* 96 28 */ After: struct sctp_transport { struct list_head transports; /* 0 16 */ atomic_t refcnt; /* 16 4 */ __u32 dead:1; /* 20:31 4 */ __u32 rto_pending:1; /* 20:30 4 */ __u32 hb_sent:1; /* 20:29 4 */ __u32 pmtu_pending:1; /* 20:28 4 */ /* XXX 28 bits hole, try to pack */ __u32 sack_generation; /* 24 4 */ u32 dst_cookie; /* 28 4 */ struct flowi fl; /* 32 64 */ /* --- cacheline 1 boundary (64 bytes) was 32 bytes ago --- */ union sctp_addr ipaddr; /* 96 28 */ Signed-off-by: Fan Du Acked-by: Neil Horman Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index c0f4e29eedd5..d9c93a77b1a9 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -782,6 +782,7 @@ struct sctp_transport { /* Has this transport moved the ctsn since we last sacked */ __u32 sack_generation; + u32 dst_cookie; struct flowi fl; @@ -946,7 +947,6 @@ struct sctp_transport { __u64 hb_nonce; struct rcu_head rcu; - u32 dst_cookie; }; struct sctp_transport *sctp_transport_new(struct net *, const union sctp_addr *, -- cgit v1.2.3-71-gd317 From ce157510eb886feb996a7a13354973bdf4aae1a1 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 11 Apr 2013 01:34:59 -0700 Subject: mtd: nand: reword nand_chip bad block interface comments This remedies a few problems: (1) The use of "the" vs. "a" is a little confusing, IMO. (2) nand_chip.block_bad is used exclusively for checking the OOB bad block markers of a NAND. Any BBT functionality is handled in nand_bbt.c, so this description should differentiate itself from nand_bbt.c. Signed-off-by: Brian Norris Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- include/linux/mtd/nand.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index ab6363443ce8..50c5ea02c81a 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -390,8 +390,8 @@ struct nand_buffers { * @write_buf: [REPLACEABLE] write data from the buffer to the chip * @read_buf: [REPLACEABLE] read data from the chip into the buffer * @select_chip: [REPLACEABLE] select chip nr - * @block_bad: [REPLACEABLE] check, if the block is bad - * @block_markbad: [REPLACEABLE] mark the block bad + * @block_bad: [REPLACEABLE] check if a block is bad, using OOB markers + * @block_markbad: [REPLACEABLE] mark a block bad * @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific function for controlling * ALE/CLE/nCE. Also used to write command and address * @init_size: [BOARDSPECIFIC] hardwarespecific function for setting -- cgit v1.2.3-71-gd317 From 52778b2e9fcb66c8f1c9d5b1ae435815c19e7ae3 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Wed, 15 May 2013 16:40:25 +0800 Subject: mtd: increase max OOB size to 744 The oob size of Micron's MT29F64G08CBABAWP is 744 bytes. So increase the NAND_MAX_OOBSIZE to 744. Signed-off-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- include/linux/mtd/nand.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 50c5ea02c81a..38535eb453db 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -56,7 +56,7 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); * is supported now. If you add a chip with bigger oobsize/page * adjust this accordingly. */ -#define NAND_MAX_OOBSIZE 640 +#define NAND_MAX_OOBSIZE 744 #define NAND_MAX_PAGESIZE 8192 /* -- cgit v1.2.3-71-gd317 From 64ddba4d8a381b65bebee24c8da4eb80080c64a4 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Mon, 29 Apr 2013 14:07:48 +0200 Subject: mtd: nand: fsmc: update of OF support Add nand bank selection and timings to the device tree bindings. Signed-off-by: Mian Yousaf Kaukab [Added some documentation] Signed-off-by: Linus Walleij Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- .../devicetree/bindings/mtd/fsmc-nand.txt | 25 +++++++++++++++++++++- drivers/mtd/nand/fsmc_nand.c | 18 ++++++++++++++++ include/linux/mtd/fsmc.h | 1 + 3 files changed, 43 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/mtd/fsmc-nand.txt b/Documentation/devicetree/bindings/mtd/fsmc-nand.txt index 2240ac09f6ba..ec42935f3908 100644 --- a/Documentation/devicetree/bindings/mtd/fsmc-nand.txt +++ b/Documentation/devicetree/bindings/mtd/fsmc-nand.txt @@ -1,4 +1,5 @@ -* FSMC NAND +ST Microelectronics Flexible Static Memory Controller (FSMC) +NAND Interface Required properties: - compatible : "st,spear600-fsmc-nand", "stericsson,fsmc-nand" @@ -9,6 +10,26 @@ Optional properties: - bank-width : Width (in bytes) of the device. If not present, the width defaults to 1 byte - nand-skip-bbtscan: Indicates the the BBT scanning should be skipped +- timings: array of 6 bytes for NAND timings. The meanings of these bytes + are: + byte 0 TCLR : CLE to RE delay in number of AHB clock cycles, only 4 bits + are valid. Zero means one clockcycle, 15 means 16 clock + cycles. + byte 1 TAR : ALE to RE delay, 4 bits are valid. Same format as TCLR. + byte 2 THIZ : number of HCLK clock cycles during which the data bus is + kept in Hi-Z (tristate) after the start of a write access. + Only valid for write transactions. Zero means zero cycles, + 255 means 255 cycles. + byte 3 THOLD : number of HCLK clock cycles to hold the address (and data + when writing) after the command deassertation. Zero means + one cycle, 255 means 256 cycles. + byte 4 TWAIT : number of HCLK clock cycles to assert the command to the + NAND flash in response to SMWAITn. Zero means 1 cycle, + 255 means 256 cycles. + byte 5 TSET : number of HCLK clock cycles to assert the address before the + command is asserted. Zero means one cycle, 255 means 256 + cycles. +- bank: default NAND bank to use (0-3 are valid, 0 is the default). Example: @@ -24,6 +45,8 @@ Example: bank-width = <1>; nand-skip-bbtscan; + timings = /bits/ 8 <0 0 0 2 3 0>; + bank = <1>; partition@0 { ... diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 0e5a1d96a721..037838682f40 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -889,6 +889,24 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev, if (of_get_property(np, "nand-skip-bbtscan", NULL)) pdata->options = NAND_SKIP_BBTSCAN; + pdata->nand_timings = devm_kzalloc(&pdev->dev, + sizeof(*pdata->nand_timings), GFP_KERNEL); + if (!pdata->nand_timings) { + dev_err(&pdev->dev, "no memory for nand_timing\n"); + return -ENOMEM; + } + of_property_read_u8_array(np, "timings", (u8 *)pdata->nand_timings, + sizeof(*pdata->nand_timings)); + + /* Set default NAND bank to 0 */ + pdata->bank = 0; + if (!of_property_read_u32(np, "bank", &val)) { + if (val > 3) { + dev_err(&pdev->dev, "invalid bank %u\n", val); + return -EINVAL; + } + pdata->bank = val; + } return 0; } #else diff --git a/include/linux/mtd/fsmc.h b/include/linux/mtd/fsmc.h index d6ed61ef451d..c8be32e9fc49 100644 --- a/include/linux/mtd/fsmc.h +++ b/include/linux/mtd/fsmc.h @@ -137,6 +137,7 @@ enum access_mode { /** * fsmc_nand_platform_data - platform specific NAND controller config + * @nand_timings: timing setup for the physical NAND interface * @partitions: partition table for the platform, use a default fallback * if this is NULL * @nr_partitions: the number of partitions in the previous entry -- cgit v1.2.3-71-gd317 From d914c932d7764bf9633af974940aee9390adf20d Mon Sep 17 00:00:00 2001 From: David Mosberger Date: Wed, 29 May 2013 15:30:13 +0300 Subject: mtd: nand_base: Only use GET/SET FEATURES command on chips that support them. Spansion's S34MLx chips support ONFI but not the GET/SET FEATURES calls. Signed-off-by: David Mosberger Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 8 ++++++-- include/linux/mtd/nand.h | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index edc7663f4b7b..1cbacffb5106 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2709,7 +2709,9 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, { int status; - if (!chip->onfi_version) + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) return -EINVAL; chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1); @@ -2730,7 +2732,9 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, int addr, uint8_t *subfeature_param) { - if (!chip->onfi_version) + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) return -EINVAL; /* clear the sub feature parameters */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 38535eb453db..0745a42f1acd 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -217,6 +217,9 @@ struct nand_chip; /* ONFI subfeature parameters length */ #define ONFI_SUBFEATURE_PARAM_LEN 4 +/* ONFI optional commands SET/GET FEATURES supported? */ +#define ONFI_OPT_CMD_SET_GET_FEATURES (1 << 2) + struct nand_onfi_params { /* rev info and features block */ /* 'O' 'N' 'F' 'I' */ -- cgit v1.2.3-71-gd317 From 71b94e2e866aa35f40945d9e820fc3214b792d1f Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Thu, 9 May 2013 15:34:54 +0800 Subject: mtd: atmel_nand: replace cpu_is_at32ap7000() with a nand platform data The nand driver use cpu_is_at32ap7000() macro for a workaround. For the multi-platform support, we will remove this cpu_is_xxx() macro. This patch adds a boolean variable need_reset_workaround in structure atmel_nand_data. Using this variable we can remove cpu_is_at32ap7000() macro. Hans-Christian: Feel free to push this through the mtd tree, if they won't accept it I'm working on getting my workflow up on the linux-avr32.git tree. Signed-off-by: Josh Wu Acked-by: Hans-Christian Egtvedt Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- arch/avr32/mach-at32ap/at32ap700x.c | 3 +++ drivers/mtd/nand/atmel_nand.c | 13 ++++++------- include/linux/platform_data/atmel.h | 3 +++ 3 files changed, 12 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index 7f8759a8a92a..a68f3cf7c3c1 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -1983,6 +1983,9 @@ at32_add_device_nand(unsigned int id, struct atmel_nand_data *data) ARRAY_SIZE(smc_cs3_resource))) goto fail; + /* For at32ap7000, we use the reset workaround for nand driver */ + data->need_reset_workaround = true; + if (platform_device_add_data(pdev, data, sizeof(struct atmel_nand_data))) goto fail; diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 2d23d2929438..7bf912b5b969 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1174,10 +1174,9 @@ static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, * Workaround: Reset the parity registers before reading the * actual data. */ - if (cpu_is_at32ap7000()) { - struct atmel_nand_host *host = chip->priv; + struct atmel_nand_host *host = chip->priv; + if (host->board.need_reset_workaround) ecc_writel(host->ecc, CR, ATMEL_ECC_RST); - } /* read the page */ chip->read_buf(mtd, p, eccsize); @@ -1298,11 +1297,11 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat, */ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) { - if (cpu_is_at32ap7000()) { - struct nand_chip *nand_chip = mtd->priv; - struct atmel_nand_host *host = nand_chip->priv; + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + + if (host->board.need_reset_workaround) ecc_writel(host->ecc, CR, ATMEL_ECC_RST); - } } #if defined(CONFIG_OF) diff --git a/include/linux/platform_data/atmel.h b/include/linux/platform_data/atmel.h index 6a293b7fff3b..59f558d9b81e 100644 --- a/include/linux/platform_data/atmel.h +++ b/include/linux/platform_data/atmel.h @@ -71,6 +71,9 @@ struct atmel_nand_data { u8 on_flash_bbt; /* bbt on flash */ struct mtd_partition *parts; unsigned int num_parts; + + /* default is false, only for at32ap7000 chip is true */ + bool need_reset_workaround; }; /* Serial */ -- cgit v1.2.3-71-gd317 From 1b7192658a08f70df0f290634fd7cd2ecb629fc9 Mon Sep 17 00:00:00 2001 From: Josh Wu Date: Thu, 9 May 2013 15:34:55 +0800 Subject: mtd: atmel_nand: add a new dt binding item for nand dma support This patch will set the nand dma support in dts. Since we will not use cpu_is_xxx() in nand driver. We needn't include the mach/cpu.h any more. Signed-off-by: Josh Wu Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- Documentation/devicetree/bindings/mtd/atmel-nand.txt | 1 + drivers/mtd/nand/atmel_nand.c | 11 +++-------- include/linux/platform_data/atmel.h | 1 + 3 files changed, 5 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt index d555421ea49f..b6eb484366a5 100644 --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt @@ -15,6 +15,7 @@ Required properties: optional gpio and may be set to 0 if not present. Optional properties: +- atmel,nand-has-dma : boolean to support dma transfer for nand read/write. - nand-ecc-mode : String, operation mode of the NAND ecc mode, soft by default. Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first", "soft_bch". diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 7bf912b5b969..61d38697986e 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -43,8 +43,6 @@ #include #include -#include - static int use_dma = 1; module_param(use_dma, int, 0); @@ -128,11 +126,6 @@ struct atmel_nand_host { static struct nand_ecclayout atmel_pmecc_oobinfo; -static int cpu_has_dma(void) -{ - return cpu_is_at91sam9rl() || cpu_is_at91sam9g45(); -} - /* * Enable NAND. */ @@ -1336,6 +1329,8 @@ static int atmel_of_init_port(struct atmel_nand_host *host, board->on_flash_bbt = of_get_nand_on_flash_bbt(np); + board->has_dma = of_property_read_bool(np, "atmel,nand-has-dma"); + if (of_get_nand_bus_width(np) == 16) board->bus_width_16 = 1; @@ -1600,7 +1595,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) nand_chip->bbt_options |= NAND_BBT_USE_FLASH; } - if (!cpu_has_dma()) + if (!host->board.has_dma) use_dma = 0; if (use_dma) { diff --git a/include/linux/platform_data/atmel.h b/include/linux/platform_data/atmel.h index 59f558d9b81e..cea9f70133c5 100644 --- a/include/linux/platform_data/atmel.h +++ b/include/linux/platform_data/atmel.h @@ -71,6 +71,7 @@ struct atmel_nand_data { u8 on_flash_bbt; /* bbt on flash */ struct mtd_partition *parts; unsigned int num_parts; + bool has_dma; /* support dma transfer */ /* default is false, only for at32ap7000 chip is true */ bool need_reset_workaround; -- cgit v1.2.3-71-gd317 From e7f1935c11269bc53cd52425b1025657adddb839 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 25 Jul 2013 21:45:17 +0200 Subject: wireless: make TU conversion macros available A few places in the code (mac80211 and iwlmvm) use the same TU_TO_JIFFIES() macro and could use TU_TO_EXP_TIME() that mac80211 has. Make these available to everyone and use them. Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/mvm/time-event.c | 7 ++----- include/linux/ieee80211.h | 4 ++++ net/mac80211/ieee80211_i.h | 3 --- 3 files changed, 6 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index ad9bbca99213..9f100363b177 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -73,7 +73,6 @@ #include "iwl-prph.h" /* A TimeUnit is 1024 microsecond */ -#define TU_TO_JIFFIES(_tu) (usecs_to_jiffies((_tu) * 1024)) #define MSEC_TO_TU(_msec) (_msec*1000/1024) /* @@ -191,8 +190,7 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, iwl_mvm_te_clear_data(mvm, te_data); } else if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_START) { te_data->running = true; - te_data->end_jiffies = jiffies + - TU_TO_JIFFIES(te_data->duration); + te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration); if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); @@ -329,8 +327,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); if (te_data->running && - time_after(te_data->end_jiffies, - jiffies + TU_TO_JIFFIES(min_duration))) { + time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) { IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n", jiffies_to_msecs(te_data->end_jiffies - jiffies)); return; diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index b3ce299782af..23a8877f4ded 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2288,4 +2288,8 @@ static inline bool ieee80211_check_tim(const struct ieee80211_tim_ie *tim, return !!(tim->virtual_map[index] & mask); } +/* convert time units */ +#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024)) +#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x)) + #endif /* LINUX_IEEE80211_H */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index e94c84050e9c..b6186517ec56 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -53,9 +53,6 @@ struct ieee80211_local; * increased memory use (about 2 kB of RAM per entry). */ #define IEEE80211_FRAGMENT_MAX 4 -#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024)) -#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x)) - /* power level hasn't been configured (or set to automatic) */ #define IEEE80211_UNSET_POWER_LEVEL INT_MIN -- cgit v1.2.3-71-gd317 From caf5c03f17c33a14ef0e7033000f89e4d0910f5a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 30 Jul 2013 14:38:34 +0200 Subject: ACPI: Move acpi_bus_get_device() from bus.c to scan.c Move acpi_bus_get_device() from bus.c to scan.c which allows acpi_bus_data_handler() to become static and clean up the latter. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 21 --------------------- drivers/acpi/scan.c | 30 ++++++++++++++++++++++-------- include/acpi/acpi_bus.h | 1 - 3 files changed, 22 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 25b289ff4290..7df97d277545 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -89,27 +89,6 @@ static struct dmi_system_id dsdt_dmi_table[] __initdata = { Device Management -------------------------------------------------------------------------- */ -int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) -{ - acpi_status status; - - if (!device) - return -EINVAL; - - /* TBD: Support fixed-feature devices */ - - status = acpi_get_data(handle, acpi_bus_data_handler, (void **)device); - if (ACPI_FAILURE(status) || !*device) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n", - handle)); - return -ENODEV; - } - - return 0; -} - -EXPORT_SYMBOL(acpi_bus_get_device); - acpi_status acpi_bus_get_status_handle(acpi_handle handle, unsigned long long *sta) { diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 10985573aaa7..4b2679342e82 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -997,6 +997,28 @@ struct bus_type acpi_bus_type = { .uevent = acpi_device_uevent, }; +static void acpi_bus_data_handler(acpi_handle handle, void *context) +{ + /* Intentionally empty. */ +} + +int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) +{ + acpi_status status; + + if (!device) + return -EINVAL; + + status = acpi_get_data(handle, acpi_bus_data_handler, (void **)device); + if (ACPI_FAILURE(status) || !*device) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n", + handle)); + return -ENODEV; + } + return 0; +} +EXPORT_SYMBOL_GPL(acpi_bus_get_device); + int acpi_device_add(struct acpi_device *device, void (*release)(struct device *)) { @@ -1208,14 +1230,6 @@ acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd) } EXPORT_SYMBOL_GPL(acpi_bus_get_ejd); -void acpi_bus_data_handler(acpi_handle handle, void *context) -{ - - /* TBD */ - - return; -} - static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, struct acpi_device_wakeup *wakeup) { diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 2650d1f19e43..2a42cb36d238 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -363,7 +363,6 @@ extern void unregister_acpi_bus_notifier(struct notifier_block *nb); */ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device); -void acpi_bus_data_handler(acpi_handle handle, void *context); acpi_status acpi_bus_get_status_handle(acpi_handle handle, unsigned long long *sta); int acpi_bus_get_status(struct acpi_device *device); -- cgit v1.2.3-71-gd317 From 6a63b098f0ea34a2cdfea11a5c5f89e723c862c7 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 26 Jul 2013 10:17:39 -0300 Subject: bus: mvebu-mbus: Add new API for window creation We add an API to create MBus address decoding windows from the target ID and attribute. This function will be used later and deprecate the current name based scheme. Signed-off-by: Thomas Petazzoni Tested-by: Andrew Lunn Tested-by: Sebastian Hesselbarth Signed-off-by: Jason Cooper --- drivers/bus/mvebu-mbus.c | 33 +++++++++++++++++++++++++-------- include/linux/mbus.h | 6 ++++++ 2 files changed, 31 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 33c6947eebec..827468abb236 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -748,6 +748,22 @@ static const struct of_device_id of_mvebu_mbus_ids[] = { /* * Public API of the driver */ +int mvebu_mbus_add_window_remap_by_id(unsigned int target, + unsigned int attribute, + phys_addr_t base, size_t size, + phys_addr_t remap) +{ + struct mvebu_mbus_state *s = &mbus_state; + + if (!mvebu_mbus_window_conflicts(s, base, size, target, attribute)) { + pr_err("cannot add window '%x:%x', conflicts with another window\n", + target, attribute); + return -EINVAL; + } + + return mvebu_mbus_alloc_window(s, base, size, remap, target, attribute); +} + int mvebu_mbus_add_window_remap_flags(const char *devname, phys_addr_t base, size_t size, phys_addr_t remap, unsigned int flags) @@ -776,14 +792,8 @@ int mvebu_mbus_add_window_remap_flags(const char *devname, phys_addr_t base, else if (flags == MVEBU_MBUS_PCI_WA) attr |= 0x28; - if (!mvebu_mbus_window_conflicts(s, base, size, target, attr)) { - pr_err("cannot add window '%s', conflicts with another window\n", - devname); - return -EINVAL; - } - - return mvebu_mbus_alloc_window(s, base, size, remap, target, attr); - + return mvebu_mbus_add_window_remap_by_id(target, attr, base, + size, remap); } int mvebu_mbus_add_window(const char *devname, phys_addr_t base, size_t size) @@ -792,6 +802,13 @@ int mvebu_mbus_add_window(const char *devname, phys_addr_t base, size_t size) MVEBU_MBUS_NO_REMAP, 0); } +int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute, + phys_addr_t base, size_t size) +{ + return mvebu_mbus_add_window_remap_by_id(target, attribute, base, + size, MVEBU_MBUS_NO_REMAP); +} + int mvebu_mbus_del_window(phys_addr_t base, size_t size) { int win; diff --git a/include/linux/mbus.h b/include/linux/mbus.h index dba482e31a13..9245b663e720 100644 --- a/include/linux/mbus.h +++ b/include/linux/mbus.h @@ -62,8 +62,14 @@ static inline const struct mbus_dram_target_info *mv_mbus_dram_info(void) int mvebu_mbus_add_window_remap_flags(const char *devname, phys_addr_t base, size_t size, phys_addr_t remap, unsigned int flags); +int mvebu_mbus_add_window_remap_by_id(unsigned int target, + unsigned int attribute, + phys_addr_t base, size_t size, + phys_addr_t remap); int mvebu_mbus_add_window(const char *devname, phys_addr_t base, size_t size); +int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute, + phys_addr_t base, size_t size); int mvebu_mbus_del_window(phys_addr_t base, size_t size); int mvebu_mbus_init(const char *soc, phys_addr_t mbus_phys_base, size_t mbus_size, phys_addr_t sdram_phys_base, -- cgit v1.2.3-71-gd317 From 6839cfa82f99fd098ea486e7f9df78344c8e091f Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 26 Jul 2013 10:17:45 -0300 Subject: bus: mvebu-mbus: Introduce device tree binding This patch adds the most fundamental device-tree initialization. We only introduce what's required to be able to probe the mvebu-mbus driver from the DT. Follow-up patches will extend the device tree binding, allowing to describe static address decoding windows. Signed-off-by: Thomas Petazzoni Signed-off-by: Ezequiel Garcia Tested-by: Andrew Lunn Tested-by: Sebastian Hesselbarth Signed-off-by: Jason Cooper --- drivers/bus/mvebu-mbus.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mbus.h | 1 + 2 files changed, 50 insertions(+) (limited to 'include') diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 1b17954600aa..44a07c4ee4ea 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -900,3 +900,52 @@ int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base, sdramwins_phys_base, sdramwins_size); } + +#ifdef CONFIG_OF +int __init mvebu_mbus_dt_init(void) +{ + struct resource mbuswins_res, sdramwins_res; + struct device_node *np, *controller; + const struct of_device_id *of_id; + const __be32 *prop; + int ret; + + np = of_find_matching_node(NULL, of_mvebu_mbus_ids); + if (!np) { + pr_err("could not find a matching SoC family\n"); + return -ENODEV; + } + + of_id = of_match_node(of_mvebu_mbus_ids, np); + mbus_state.soc = of_id->data; + + prop = of_get_property(np, "controller", NULL); + if (!prop) { + pr_err("required 'controller' property missing\n"); + return -EINVAL; + } + + controller = of_find_node_by_phandle(be32_to_cpup(prop)); + if (!controller) { + pr_err("could not find an 'mbus-controller' node\n"); + return -ENODEV; + } + + if (of_address_to_resource(controller, 0, &mbuswins_res)) { + pr_err("cannot get MBUS register address\n"); + return -EINVAL; + } + + if (of_address_to_resource(controller, 1, &sdramwins_res)) { + pr_err("cannot get SDRAM register address\n"); + return -EINVAL; + } + + ret = mvebu_mbus_common_init(&mbus_state, + mbuswins_res.start, + resource_size(&mbuswins_res), + sdramwins_res.start, + resource_size(&sdramwins_res)); + return ret; +} +#endif diff --git a/include/linux/mbus.h b/include/linux/mbus.h index 9245b663e720..eadefd687a0b 100644 --- a/include/linux/mbus.h +++ b/include/linux/mbus.h @@ -74,5 +74,6 @@ int mvebu_mbus_del_window(phys_addr_t base, size_t size); int mvebu_mbus_init(const char *soc, phys_addr_t mbus_phys_base, size_t mbus_size, phys_addr_t sdram_phys_base, size_t sdram_size); +int mvebu_mbus_dt_init(void); #endif /* __LINUX_MBUS_H */ -- cgit v1.2.3-71-gd317 From 79d946837c042fba3e9ba2726f3cfa56aa408e16 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 26 Jul 2013 10:17:47 -0300 Subject: bus: mvebu-mbus: Add new API for the PCIe memory and IO aperture We add two optional properties to the MBus DT binding, to encode the PCIe memory and IO aperture. This allows such information to be retrieved by -for instance- the pci driver to allocate the MBus decoding windows. Correspondingly, and in order to retrieve this information, we add two new APIs. Signed-off-by: Ezequiel Garcia Tested-by: Andrew Lunn Tested-by: Sebastian Hesselbarth Signed-off-by: Jason Cooper --- drivers/bus/mvebu-mbus.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mbus.h | 4 ++++ 2 files changed, 53 insertions(+) (limited to 'include') diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 78b8c0436f0b..929fed1f6eb9 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -142,6 +142,8 @@ struct mvebu_mbus_state { struct dentry *debugfs_root; struct dentry *debugfs_sdram; struct dentry *debugfs_devs; + struct resource pcie_mem_aperture; + struct resource pcie_io_aperture; const struct mvebu_mbus_soc_data *soc; int hw_io_coherency; }; @@ -821,6 +823,20 @@ int mvebu_mbus_del_window(phys_addr_t base, size_t size) return 0; } +void mvebu_mbus_get_pcie_mem_aperture(struct resource *res) +{ + if (!res) + return; + *res = mbus_state.pcie_mem_aperture; +} + +void mvebu_mbus_get_pcie_io_aperture(struct resource *res) +{ + if (!res) + return; + *res = mbus_state.pcie_io_aperture; +} + static __init int mvebu_mbus_debugfs_init(void) { struct mvebu_mbus_state *s = &mbus_state; @@ -1023,6 +1039,35 @@ static int __init mbus_dt_setup(struct mvebu_mbus_state *mbus, return 0; } +static void __init mvebu_mbus_get_pcie_resources(struct device_node *np, + struct resource *mem, + struct resource *io) +{ + u32 reg[2]; + int ret; + + /* + * These are optional, so we clear them and they'll + * be zero if they are missing from the DT. + */ + memset(mem, 0, sizeof(struct resource)); + memset(io, 0, sizeof(struct resource)); + + ret = of_property_read_u32_array(np, "pcie-mem-aperture", reg, ARRAY_SIZE(reg)); + if (!ret) { + mem->start = reg[0]; + mem->end = mem->start + reg[1]; + mem->flags = IORESOURCE_MEM; + } + + ret = of_property_read_u32_array(np, "pcie-io-aperture", reg, ARRAY_SIZE(reg)); + if (!ret) { + io->start = reg[0]; + io->end = io->start + reg[1]; + io->flags = IORESOURCE_IO; + } +} + int __init mvebu_mbus_dt_init(void) { struct resource mbuswins_res, sdramwins_res; @@ -1062,6 +1107,10 @@ int __init mvebu_mbus_dt_init(void) return -EINVAL; } + /* Get optional pcie-{mem,io}-aperture properties */ + mvebu_mbus_get_pcie_resources(np, &mbus_state.pcie_mem_aperture, + &mbus_state.pcie_io_aperture); + ret = mvebu_mbus_common_init(&mbus_state, mbuswins_res.start, resource_size(&mbuswins_res), diff --git a/include/linux/mbus.h b/include/linux/mbus.h index eadefd687a0b..650bc154a86e 100644 --- a/include/linux/mbus.h +++ b/include/linux/mbus.h @@ -11,6 +11,8 @@ #ifndef __LINUX_MBUS_H #define __LINUX_MBUS_H +struct resource; + struct mbus_dram_target_info { /* @@ -59,6 +61,8 @@ static inline const struct mbus_dram_target_info *mv_mbus_dram_info(void) } #endif +void mvebu_mbus_get_pcie_mem_aperture(struct resource *res); +void mvebu_mbus_get_pcie_io_aperture(struct resource *res); int mvebu_mbus_add_window_remap_flags(const char *devname, phys_addr_t base, size_t size, phys_addr_t remap, unsigned int flags); -- cgit v1.2.3-71-gd317 From 124e5427f59877cd9dde5fa2ea90c413765e77ef Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 26 Jul 2013 10:17:50 -0300 Subject: bus: mvebu-mbus: Remove the no longer used name-based API Now that every user of the deprecated name-based API has been converted to using the ID-based API, let's remove the former one. Signed-off-by: Thomas Petazzoni Tested-by: Andrew Lunn Tested-by: Sebastian Hesselbarth Signed-off-by: Jason Cooper --- drivers/bus/mvebu-mbus.c | 38 -------------------------------------- include/linux/mbus.h | 5 ----- 2 files changed, 43 deletions(-) (limited to 'include') diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 929fed1f6eb9..b4a738236987 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -766,44 +766,6 @@ int mvebu_mbus_add_window_remap_by_id(unsigned int target, return mvebu_mbus_alloc_window(s, base, size, remap, target, attribute); } -int mvebu_mbus_add_window_remap_flags(const char *devname, phys_addr_t base, - size_t size, phys_addr_t remap, - unsigned int flags) -{ - struct mvebu_mbus_state *s = &mbus_state; - u8 target, attr; - int i; - - if (!s->soc->map) - return -ENODEV; - - for (i = 0; s->soc->map[i].name; i++) - if (!strcmp(s->soc->map[i].name, devname)) - break; - - if (!s->soc->map[i].name) { - pr_err("unknown device '%s'\n", devname); - return -ENODEV; - } - - target = s->soc->map[i].target; - attr = s->soc->map[i].attr; - - if (flags == MVEBU_MBUS_PCI_MEM) - attr |= 0x8; - else if (flags == MVEBU_MBUS_PCI_WA) - attr |= 0x28; - - return mvebu_mbus_add_window_remap_by_id(target, attr, base, - size, remap); -} - -int mvebu_mbus_add_window(const char *devname, phys_addr_t base, size_t size) -{ - return mvebu_mbus_add_window_remap_flags(devname, base, size, - MVEBU_MBUS_NO_REMAP, 0); -} - int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute, phys_addr_t base, size_t size) { diff --git a/include/linux/mbus.h b/include/linux/mbus.h index 650bc154a86e..345b8c53b897 100644 --- a/include/linux/mbus.h +++ b/include/linux/mbus.h @@ -63,15 +63,10 @@ static inline const struct mbus_dram_target_info *mv_mbus_dram_info(void) void mvebu_mbus_get_pcie_mem_aperture(struct resource *res); void mvebu_mbus_get_pcie_io_aperture(struct resource *res); -int mvebu_mbus_add_window_remap_flags(const char *devname, phys_addr_t base, - size_t size, phys_addr_t remap, - unsigned int flags); int mvebu_mbus_add_window_remap_by_id(unsigned int target, unsigned int attribute, phys_addr_t base, size_t size, phys_addr_t remap); -int mvebu_mbus_add_window(const char *devname, phys_addr_t base, - size_t size); int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute, phys_addr_t base, size_t size); int mvebu_mbus_del_window(phys_addr_t base, size_t size); -- cgit v1.2.3-71-gd317 From 4b4dab82340d969521f4f86108441cb597c8595d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 28 Jul 2013 18:58:29 -0700 Subject: ASoC: rsnd: remove platform dai and add dai_id on platform setting Current rsnd driver is using struct rsnd_dai_platform_info so that indicate sound DAI information (playback/capture SSI ID). But, SSI settings were also required separately. Thus, platform settings was very un-understandable. This patch adds dai_id to SSI settings, and removed rsnd_dai_platform_info. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 18 ++++++++------- sound/soc/sh/rcar/core.c | 60 +++++++++++++++++++++++++++++++++--------------- sound/soc/sh/rcar/gen.c | 10 ++++---- sound/soc/sh/rcar/rsnd.h | 3 +++ sound/soc/sh/rcar/ssi.c | 22 ++++++++++++++++++ 5 files changed, 82 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 99d8dd029906..33233edd1664 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -28,15 +28,24 @@ /* * flags * - * 0xA0000000 + * 0xAB000000 * * A : clock sharing settings + * B : SSI direction */ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ #define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ +#define RSND_SSI_PLAY (1 << 24) + +#define RSND_SSI_SET(_dai_id, _pio_irq, _flags) \ +{ .dai_id = _dai_id, .pio_irq = _pio_irq, .flags = _flags } +#define RSND_SSI_UNUSED \ +{ .dai_id = -1, .pio_irq = -1, .flags = 0 } + struct rsnd_ssi_platform_info { + int dai_id; int pio_irq; u32 flags; }; @@ -45,11 +54,6 @@ struct rsnd_scu_platform_info { u32 flags; }; -struct rsnd_dai_platform_info { - int ssi_id_playback; - int ssi_id_capture; -}; - /* * flags * @@ -66,8 +70,6 @@ struct rcar_snd_info { int ssi_info_nr; struct rsnd_scu_platform_info *scu_info; int scu_info_nr; - struct rsnd_dai_platform_info *dai_info; - int dai_info_nr; int (*start)(int id); int (*stop)(int id); }; diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 9a5469d3f352..420d6df9c3d0 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -219,6 +219,16 @@ int rsnd_dai_disconnect(struct rsnd_mod *mod) return 0; } +int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai) +{ + int id = rdai - priv->rdai; + + if ((id < 0) || (id >= rsnd_dai_nr(priv))) + return -EINVAL; + + return id; +} + struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id) { return priv->rdai + id; @@ -315,9 +325,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); - struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); - int ssi_id = rsnd_dai_is_play(rdai, io) ? info->ssi_id_playback : - info->ssi_id_capture; + struct rsnd_mod *mod = rsnd_ssi_mod_get_frm_dai(priv, + rsnd_dai_id(priv, rdai), + rsnd_dai_is_play(rdai, io)); + int ssi_id = rsnd_mod_id(mod); int ret; unsigned long flags; @@ -439,10 +450,24 @@ static int rsnd_dai_probe(struct platform_device *pdev, { struct snd_soc_dai_driver *drv; struct rsnd_dai *rdai; + struct rsnd_mod *pmod, *cmod; struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dai_platform_info *dai_info; - int dai_nr = info->dai_info_nr; - int i, pid, cid; + int dai_nr; + int i; + + /* get max dai nr */ + for (dai_nr = 0; dai_nr < 32; dai_nr++) { + pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1); + cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0); + + if (!pmod && !cmod) + break; + } + + if (!dai_nr) { + dev_err(dev, "no dai\n"); + return -EIO; + } drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL); rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL); @@ -452,10 +477,9 @@ static int rsnd_dai_probe(struct platform_device *pdev, } for (i = 0; i < dai_nr; i++) { - dai_info = &info->dai_info[i]; - pid = dai_info->ssi_id_playback; - cid = dai_info->ssi_id_capture; + pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1); + cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0); /* * init rsnd_dai @@ -463,8 +487,6 @@ static int rsnd_dai_probe(struct platform_device *pdev, INIT_LIST_HEAD(&rdai[i].playback.head); INIT_LIST_HEAD(&rdai[i].capture.head); - rdai[i].info = dai_info; - snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i); /* @@ -472,20 +494,22 @@ static int rsnd_dai_probe(struct platform_device *pdev, */ drv[i].name = rdai[i].name; drv[i].ops = &rsnd_soc_dai_ops; - if (pid >= 0) { + if (pmod) { drv[i].playback.rates = RSND_RATES; drv[i].playback.formats = RSND_FMTS; drv[i].playback.channels_min = 2; drv[i].playback.channels_max = 2; } - if (cid >= 0) { + if (cmod) { drv[i].capture.rates = RSND_RATES; drv[i].capture.formats = RSND_FMTS; drv[i].capture.channels_min = 2; drv[i].capture.channels_max = 2; } - dev_dbg(dev, "%s (%d, %d) probed", rdai[i].name, pid, cid); + dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name, + pmod ? "play" : " -- ", + cmod ? "capture" : " -- "); } priv->dai_nr = dai_nr; @@ -627,10 +651,6 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = rsnd_dai_probe(pdev, info, priv); - if (ret < 0) - return ret; - ret = rsnd_scu_probe(pdev, info, priv); if (ret < 0) return ret; @@ -643,6 +663,10 @@ static int rsnd_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = rsnd_dai_probe(pdev, info, priv); + if (ret < 0) + return ret; + /* * asoc register */ diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 61232cd9908f..460c57eef267 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -49,7 +49,6 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) { - struct rsnd_dai_platform_info *info = rsnd_dai_get_platform_info(rdai); struct rsnd_mod *mod; int ret; int id; @@ -67,10 +66,11 @@ static int rsnd_gen1_path_init(struct rsnd_priv *priv, * Then, SSI id = SCU id here */ - if (rsnd_dai_is_play(rdai, io)) - id = info->ssi_id_playback; - else - id = info->ssi_id_capture; + /* get SSI's ID */ + mod = rsnd_ssi_mod_get_frm_dai(priv, + rsnd_dai_id(priv, rdai), + rsnd_dai_is_play(rdai, io)); + id = rsnd_mod_id(mod); /* SSI */ mod = rsnd_ssi_mod_get(priv, id); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 0e7727cc41db..9243e387104c 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -157,6 +157,7 @@ int rsnd_dai_disconnect(struct rsnd_mod *mod); int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod, struct rsnd_dai_stream *io); int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); +int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai); #define rsnd_dai_get_platform_info(rdai) ((rdai)->info) #define rsnd_io_to_runtime(io) ((io)->substream->runtime) @@ -254,5 +255,7 @@ int rsnd_ssi_probe(struct platform_device *pdev, void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); +struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, + int dai_id, int is_play); #endif diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 061ac7e88309..c48a6c7cd08e 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -87,6 +87,7 @@ struct rsnd_ssiu { #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) #define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) +#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) #define rsnd_ssi_to_ssiu(ssi)\ (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1) @@ -502,6 +503,27 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = { /* * ssi mod function */ +struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, + int dai_id, int is_play) +{ + struct rsnd_ssi *ssi; + int i, has_play; + + is_play = !!is_play; + + for_each_rsnd_ssi(ssi, priv, i) { + if (rsnd_ssi_dai_id(ssi) != dai_id) + continue; + + has_play = !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY); + + if (is_play == has_play) + return &ssi->mod; + } + + return NULL; +} + struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) { BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv)); -- cgit v1.2.3-71-gd317 From 849fc82a6f4f32b4c8c502bb7c4a68df51170232 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 28 Jul 2013 18:59:02 -0700 Subject: ASoC: rsnd: SSI supports DMA transfer This patch adds DMAEngine transfer on SSI. But, it transfers sound data from memory to SSI directly without using HPBIF at this time. It will be updated soon Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 7 +-- sound/soc/sh/rcar/ssi.c | 110 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index 33233edd1664..a72687dda0cd 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -39,13 +39,14 @@ #define RSND_SSI_PLAY (1 << 24) -#define RSND_SSI_SET(_dai_id, _pio_irq, _flags) \ -{ .dai_id = _dai_id, .pio_irq = _pio_irq, .flags = _flags } +#define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags) \ +{ .dai_id = _dai_id, .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } #define RSND_SSI_UNUSED \ -{ .dai_id = -1, .pio_irq = -1, .flags = 0 } +{ .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 } struct rsnd_ssi_platform_info { int dai_id; + int dma_id; int pio_irq; u32 flags; }; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index c48a6c7cd08e..2079ccf5f322 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -19,6 +19,7 @@ * SSICR */ #define FORCE (1 << 31) /* Fixed */ +#define DMEN (1 << 28) /* DMA Enable */ #define UIEN (1 << 27) /* Underflow Interrupt Enable */ #define OIEN (1 << 26) /* Overflow Interrupt Enable */ #define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ @@ -51,6 +52,11 @@ #define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ #define DIRQ (1 << 24) /* Data Interrupt Status Flag */ +/* + * SSIWSR + */ +#define CONT (1 << 8) /* WS Continue Function */ + struct rsnd_ssi { struct clk *clk; struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ @@ -63,6 +69,7 @@ struct rsnd_ssi { u32 cr_clk; u32 cr_etc; int err; + int dma_offset; unsigned int usrcnt; unsigned int rate; }; @@ -83,7 +90,10 @@ struct rsnd_ssiu { #define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr) #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) -#define rsnd_ssi_is_pio(ssi) ((ssi)->info->pio_irq > 0) +#define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) +#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0) +#define rsnd_ssi_dma_available(ssi) \ + rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod)) #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) #define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) @@ -477,6 +487,79 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = { .stop = rsnd_ssi_pio_stop, }; +static int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len) +{ + struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); + struct rsnd_dai_stream *io = ssi->io; + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + + *len = io->byte_per_period; + *buf = runtime->dma_addr + + rsnd_dai_pointer_offset(io, ssi->dma_offset + *len); + ssi->dma_offset = *len; /* it cares A/B plane */ + + return 0; +} + +static int rsnd_ssi_dma_complete(struct rsnd_dma *dma) +{ + struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); + struct rsnd_dai_stream *io = ssi->io; + u32 status = rsnd_mod_read(&ssi->mod, SSISR); + + rsnd_ssi_record_error(ssi, status); + + rsnd_dai_pointer_update(ssi->io, io->byte_per_period); + + return 0; +} + +static int rsnd_ssi_dma_start(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); + + /* enable DMA transfer */ + ssi->cr_etc = DMEN; + ssi->dma_offset = 0; + + rsnd_dma_start(dma); + + rsnd_ssi_hw_start(ssi, ssi->rdai, io); + + /* enable WS continue */ + if (rsnd_rdai_is_clk_master(rdai)) + rsnd_mod_write(&ssi->mod, SSIWSR, CONT); + + return 0; +} + +static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); + struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); + + ssi->cr_etc = 0; + + rsnd_ssi_hw_stop(ssi, rdai); + + rsnd_dma_stop(dma); + + return 0; +} + +static struct rsnd_mod_ops rsnd_ssi_dma_ops = { + .name = "ssi (dma)", + .init = rsnd_ssi_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_dma_start, + .stop = rsnd_ssi_dma_stop, +}; + /* * Non SSI */ @@ -573,10 +656,27 @@ int rsnd_ssi_probe(struct platform_device *pdev, ops = &rsnd_ssi_non_ops; + /* + * SSI DMA case + */ + if (pinfo->dma_id > 0) { + ret = rsnd_dma_init( + priv, rsnd_mod_to_dma(&ssi->mod), + (rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY), + pinfo->dma_id, + rsnd_ssi_dma_inquiry, + rsnd_ssi_dma_complete); + if (ret < 0) + dev_info(dev, "SSI DMA failed. try PIO transter\n"); + else + ops = &rsnd_ssi_dma_ops; + } + /* * SSI PIO case */ - if (rsnd_ssi_is_pio(ssi)) { + if (!rsnd_ssi_dma_available(ssi) && + rsnd_ssi_pio_available(ssi)) { ret = devm_request_irq(dev, pinfo->pio_irq, &rsnd_ssi_pio_interrupt, IRQF_SHARED, @@ -605,6 +705,10 @@ void rsnd_ssi_remove(struct platform_device *pdev, struct rsnd_ssi *ssi; int i; - for_each_rsnd_ssi(ssi, priv, i) + for_each_rsnd_ssi(ssi, priv, i) { clk_put(ssi->clk); + if (rsnd_ssi_dma_available(ssi)) + rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod)); + } + } -- cgit v1.2.3-71-gd317 From 374a528111fa07878090bd9694a3e153814de39c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 28 Jul 2013 18:59:12 -0700 Subject: ASoC: rsnd: SSI supports DMA transfer via BUSIF This patch adds BUSIF support for R-Car sound DMAEngine transfer. The sound data will be transferred via FIFO which can cover blank time which will happen when DMA channel is switching. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/rcar_snd.h | 6 ++ sound/soc/sh/rcar/gen.c | 10 ++- sound/soc/sh/rcar/rsnd.h | 9 +++ sound/soc/sh/rcar/scu.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++- sound/soc/sh/rcar/ssi.c | 18 +++++- 5 files changed, 190 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h index a72687dda0cd..d35412ae03b3 100644 --- a/include/sound/rcar_snd.h +++ b/include/sound/rcar_snd.h @@ -36,6 +36,7 @@ #define RSND_SSI_CLK_PIN_SHARE (1 << 31) #define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ #define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ +#define RSND_SSI_DEPENDENT (1 << 28) /* SSI needs SRU/SCU */ #define RSND_SSI_PLAY (1 << 24) @@ -51,6 +52,11 @@ struct rsnd_ssi_platform_info { u32 flags; }; +/* + * flags + */ +#define RSND_SCU_USB_HPBIF (1 << 31) /* it needs RSND_SSI_DEPENDENT */ + struct rsnd_scu_platform_info { u32 flags; }; diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 460c57eef267..babb203b43b7 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -34,9 +34,6 @@ struct rsnd_gen { #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) -#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) -#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2) - /* * Gen2 * will be filled in the future @@ -115,8 +112,15 @@ static struct rsnd_gen_ops rsnd_gen1_ops = { static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) { + RSND_GEN1_REG_MAP(gen, SRU, SRC_ROUTE_SEL, 0x0, 0x00); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL0, 0x0, 0x08); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL1, 0x0, 0x0c); + RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL2, 0x0, 0x10); + RSND_GEN1_REG_MAP(gen, SRU, SRC_CTRL, 0x0, 0xc0); RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); + RSND_GEN1_REG_MAP(gen, SRU, BUSIF_MODE, 0x4, 0x20); + RSND_GEN1_REG_MAP(gen, SRU, BUSIF_ADINR, 0x40, 0x214); RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00); RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 15dccd598960..9cc6986a8cfb 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -32,8 +32,15 @@ */ enum rsnd_reg { /* SRU/SCU */ + RSND_REG_SRC_ROUTE_SEL, + RSND_REG_SRC_TMG_SEL0, + RSND_REG_SRC_TMG_SEL1, + RSND_REG_SRC_TMG_SEL2, + RSND_REG_SRC_CTRL, RSND_REG_SSI_MODE0, RSND_REG_SSI_MODE1, + RSND_REG_BUSIF_MODE, + RSND_REG_BUSIF_ADINR, /* ADG */ RSND_REG_BRRA, @@ -213,6 +220,8 @@ int rsnd_gen_path_exit(struct rsnd_priv *priv, void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); +#define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) +#define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2) /* * R-Car ADG diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c index c12e65f240a1..29837e326bc5 100644 --- a/sound/soc/sh/rcar/scu.c +++ b/sound/soc/sh/rcar/scu.c @@ -15,6 +15,18 @@ struct rsnd_scu { struct rsnd_mod mod; }; +#define rsnd_scu_mode_flags(p) ((p)->info->flags) + +/* + * ADINR + */ +#define OTBL_24 (0 << 16) +#define OTBL_22 (2 << 16) +#define OTBL_20 (4 << 16) +#define OTBL_18 (6 << 16) +#define OTBL_16 (8 << 16) + + #define rsnd_mod_to_scu(_mod) \ container_of((_mod), struct rsnd_scu, mod) @@ -24,6 +36,116 @@ struct rsnd_scu { ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ i++) +static int rsnd_scu_set_route(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct scu_route_config { + u32 mask; + int shift; + } routes[] = { + { 0xF, 0, }, /* 0 */ + { 0xF, 4, }, /* 1 */ + { 0xF, 8, }, /* 2 */ + { 0x7, 12, }, /* 3 */ + { 0x7, 16, }, /* 4 */ + { 0x7, 20, }, /* 5 */ + { 0x7, 24, }, /* 6 */ + { 0x3, 28, }, /* 7 */ + { 0x3, 30, }, /* 8 */ + }; + + u32 mask; + u32 val; + int shift; + int id; + + /* + * Gen1 only + */ + if (!rsnd_is_gen1(priv)) + return 0; + + id = rsnd_mod_id(mod); + if (id < 0 || id > ARRAY_SIZE(routes)) + return -EIO; + + /* + * SRC_ROUTE_SELECT + */ + val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; + val = val << routes[id].shift; + mask = routes[id].mask << routes[id].shift; + + rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); + + /* + * SRC_TIMING_SELECT + */ + shift = (id % 4) * 8; + mask = 0x1F << shift; + if (8 == id) /* SRU8 is very special */ + val = id << shift; + else + val = (id + 1) << shift; + + switch (id / 4) { + case 0: + rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); + break; + case 1: + rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); + break; + case 2: + rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); + break; + } + + return 0; +} + +static int rsnd_scu_set_mode(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + int id = rsnd_mod_id(mod); + u32 val; + + if (rsnd_is_gen1(priv)) { + val = (1 << id); + rsnd_mod_bset(mod, SRC_CTRL, val, val); + } + + return 0; +} + +static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, + struct rsnd_mod *mod, + struct rsnd_dai *rdai, + struct rsnd_dai_stream *io) +{ + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 adinr = runtime->channels; + + switch (runtime->sample_bits) { + case 16: + adinr |= OTBL_16; + break; + case 32: + adinr |= OTBL_24; + break; + default: + return -EIO; + } + + rsnd_mod_write(mod, BUSIF_MODE, 1); + rsnd_mod_write(mod, BUSIF_ADINR, adinr); + + return 0; +} + static int rsnd_scu_init(struct rsnd_mod *mod, struct rsnd_dai *rdai, struct rsnd_dai_stream *io) @@ -53,9 +175,36 @@ static int rsnd_scu_start(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_scu *scu = rsnd_mod_to_scu(mod); struct device *dev = rsnd_priv_to_dev(priv); + u32 flags = rsnd_scu_mode_flags(scu); + int ret; + + /* + * SCU will be used if it has RSND_SCU_USB_HPBIF flags + */ + if (!(flags & RSND_SCU_USB_HPBIF)) { + /* it use PIO transter */ + dev_dbg(dev, "%s%d is not used\n", + rsnd_mod_name(mod), rsnd_mod_id(mod)); + + return 0; + } + + /* it use DMA transter */ + ret = rsnd_scu_set_route(priv, mod, rdai, io); + if (ret < 0) + return ret; + + ret = rsnd_scu_set_mode(priv, mod, rdai, io); + if (ret < 0) + return ret; - dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + ret = rsnd_scu_set_hpbif(priv, mod, rdai, io); + if (ret < 0) + return ret; + + dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); return 0; } @@ -112,8 +261,9 @@ int rsnd_scu_probe(struct platform_device *pdev, rsnd_mod_init(priv, &scu->mod, &rsnd_scu_ops, i); scu->info = &info->scu_info[i]; - } + dev_dbg(dev, "SCU%d probed\n", i); + } dev_dbg(dev, "scu probed\n"); return 0; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 2079ccf5f322..fae26d3f79d2 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -104,6 +104,7 @@ struct rsnd_ssiu { static void rsnd_ssi_mode_init(struct rsnd_priv *priv, struct rsnd_ssiu *ssiu) { + struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi; u32 flags; u32 val; @@ -113,8 +114,17 @@ static void rsnd_ssi_mode_init(struct rsnd_priv *priv, * SSI_MODE0 */ ssiu->ssi_mode0 = 0; - for_each_rsnd_ssi(ssi, priv, i) - ssiu->ssi_mode0 |= (1 << i); + for_each_rsnd_ssi(ssi, priv, i) { + flags = rsnd_ssi_mode_flags(ssi); + + /* see also BUSIF_MODE */ + if (!(flags & RSND_SSI_DEPENDENT)) { + ssiu->ssi_mode0 |= (1 << i); + dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", i); + } else { + dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i); + } + } /* * SSI_MODE1 @@ -670,6 +680,8 @@ int rsnd_ssi_probe(struct platform_device *pdev, dev_info(dev, "SSI DMA failed. try PIO transter\n"); else ops = &rsnd_ssi_dma_ops; + + dev_dbg(dev, "SSI%d use DMA transfer\n", i); } /* @@ -687,6 +699,8 @@ int rsnd_ssi_probe(struct platform_device *pdev, } ops = &rsnd_ssi_pio_ops; + + dev_dbg(dev, "SSI%d use PIO transfer\n", i); } rsnd_mod_init(priv, &ssi->mod, ops, i); -- cgit v1.2.3-71-gd317 From b6c035d04e80b4244a143e34e51c84cf2181bd94 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 5 Aug 2013 15:02:48 -0700 Subject: x86, asmlinkage: Make dump_stack visible dump_stack is used from assembler code, so make it visible. Signed-off-by: Andi Kleen Link: http://lkml.kernel.org/r/1375740170-7446-15-git-send-email-andi@firstfloor.org Signed-off-by: H. Peter Anvin --- include/linux/printk.h | 2 +- lib/dump_stack.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/printk.h b/include/linux/printk.h index 22c7052e9372..e6131a782481 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -200,7 +200,7 @@ static inline void show_regs_print_info(const char *log_lvl) } #endif -extern void dump_stack(void) __cold; +extern asmlinkage void dump_stack(void) __cold; #ifndef pr_fmt #define pr_fmt(fmt) fmt diff --git a/lib/dump_stack.c b/lib/dump_stack.c index c03154173cc7..f23b63f0a1c3 100644 --- a/lib/dump_stack.c +++ b/lib/dump_stack.c @@ -23,7 +23,7 @@ static void __dump_stack(void) #ifdef CONFIG_SMP static atomic_t dump_lock = ATOMIC_INIT(-1); -void dump_stack(void) +asmlinkage void dump_stack(void) { int was_locked; int old; @@ -55,7 +55,7 @@ retry: preempt_enable(); } #else -void dump_stack(void) +asmlinkage void dump_stack(void) { __dump_stack(); } -- cgit v1.2.3-71-gd317 From 3d0899e8dab638dd00a7a36b8620ec80bb812fea Mon Sep 17 00:00:00 2001 From: Gabriel Fernandez Date: Wed, 12 Jun 2013 16:35:34 +0200 Subject: ARM: u8540: Add Pinctrl Device Tree settings for uart0, uart2 This patch adds pinctrl device tree settings for uart0 and uart2 for ccu8540 board. Signed-off-by: Gabriel Fernandez Signed-off-by: Linus Walleij --- arch/arm/boot/dts/ccu8540-pinctrl.dtsi | 81 +++++++++++++++++++++++++ arch/arm/boot/dts/ccu8540.dts | 11 ++++ arch/arm/boot/dts/ste-nomadik-pinctrl.dtsi | 95 ++++++++++++++++++++++++++++++ include/dt-bindings/pinctrl/nomadik.h | 36 +++++++++++ 4 files changed, 223 insertions(+) create mode 100644 arch/arm/boot/dts/ccu8540-pinctrl.dtsi create mode 100644 arch/arm/boot/dts/ste-nomadik-pinctrl.dtsi create mode 100644 include/dt-bindings/pinctrl/nomadik.h (limited to 'include') diff --git a/arch/arm/boot/dts/ccu8540-pinctrl.dtsi b/arch/arm/boot/dts/ccu8540-pinctrl.dtsi new file mode 100644 index 000000000000..644b189f8f72 --- /dev/null +++ b/arch/arm/boot/dts/ccu8540-pinctrl.dtsi @@ -0,0 +1,81 @@ +/* + * Copyright 2012 ST-Ericsson + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include "ste-nomadik-pinctrl.dtsi" + +/ { + soc { + pinctrl { + uart0 { + uart0_default_mux: uart0_mux { + default_mux { + ste,function = "u0"; + ste,pins = "u0_a_1"; + }; + }; + + uart0_default_mode: uart0_default { + default_cfg1 { + ste,pins = "GPIO0", "GPIO2"; + ste,config = <&in_pu>; + }; + + default_cfg2 { + ste,pins = "GPIO1", "GPIO3"; + ste,config = <&out_hi>; + }; + }; + + uart0_sleep_mode: uart0_sleep { + sleep_cfg1 { + ste,pins = "GPIO0", "GPIO2"; + ste,config = <&slpm_in_pu>; + }; + + sleep_cfg2 { + ste,pins = "GPIO1", "GPIO3"; + ste,config = <&slpm_out_hi>; + }; + }; + }; + + uart2 { + uart2_default_mode: uart2_default { + default_mux { + ste,function = "u2"; + ste,pins = "u2txrx_a_1"; + }; + + default_cfg1 { + ste,pins = "GPIO120"; + ste,config = <&in_pu>; + }; + + default_cfg2 { + ste,pins = "GPIO121"; + ste,config = <&out_hi>; + }; + }; + + uart2_sleep_mode: uart2_sleep { + sleep_cfg1 { + ste,pins = "GPIO120"; + ste,config = <&slpm_in_pu>; + }; + + sleep_cfg2 { + ste,pins = "GPIO121"; + ste,config = <&slpm_out_hi>; + }; + }; + }; + }; + }; +}; diff --git a/arch/arm/boot/dts/ccu8540.dts b/arch/arm/boot/dts/ccu8540.dts index 48ff03441f5a..3065fe4f60e5 100644 --- a/arch/arm/boot/dts/ccu8540.dts +++ b/arch/arm/boot/dts/ccu8540.dts @@ -11,6 +11,7 @@ /dts-v1/; #include "dbx5x0.dtsi" +#include "ccu8540-pinctrl.dtsi" / { model = "ST-Ericsson U8540 platform with Device Tree"; @@ -21,12 +22,19 @@ }; soc { + pinctrl { + compatible = "stericsson,db8540-pinctrl"; + }; + prcmu@80157000 { reg = <0x80157000 0x2000>, <0x801b0000 0x8000>, <0x801b8000 0x3000>; reg-names = "prcmu", "prcmu-tcpm", "prcmu-tcdm"; }; uart@80120000 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&uart0_default_mux>, <&uart0_default_mode>; + pinctrl-1 = <&uart0_sleep_mode>; status = "okay"; }; @@ -35,6 +43,9 @@ }; uart@80007000 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&uart2_default_mode>; + pinctrl-1 = <&uart2_sleep_mode>; status = "okay"; }; }; diff --git a/arch/arm/boot/dts/ste-nomadik-pinctrl.dtsi b/arch/arm/boot/dts/ste-nomadik-pinctrl.dtsi new file mode 100644 index 000000000000..efddee9403c4 --- /dev/null +++ b/arch/arm/boot/dts/ste-nomadik-pinctrl.dtsi @@ -0,0 +1,95 @@ +/* + * Copyright 2012 ST-Ericsson + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#include + +/ { + in_nopull: in_nopull { + ste,input = ; + }; + + in_pu: input_pull_up { + ste,input = ; + }; + + in_pd: input_pull_down { + ste,input = ; + }; + + out_hi: output_high { + ste,output = ; + }; + + out_lo: output_low { + ste,output = ; + }; + + gpio_out_lo: gpio_output_low { + ste,gpio = ; + ste,output = ; + }; + + slpm_in_pu: slpm_in_pu { + ste,sleep = ; + ste,sleep-input = ; + ste,sleep-wakeup = ; + }; + + slpm_in_wkup_pdis: slpm_in_wkup_pdis { + ste,sleep = ; + ste,sleep-input = ; + ste,sleep-wakeup = ; + ste,sleep-pull-disable = ; + }; + + slpm_out_lo: slpm_out_lo { + ste,sleep = ; + ste,sleep-output = ; + ste,sleep-wakeup = ; + }; + + slpm_out_hi: slpm_out_hi { + ste,sleep = ; + ste,sleep-output = ; + ste,sleep-wakeup = ; + }; + + slpm_out_hi_wkup_pdis: slpm_out_hi_wkup_pdis { + ste,sleep = ; + ste,sleep-output = ; + ste,sleep-wakeup = ; + ste,sleep-pull-disable = ; + }; + + slpm_out_wkup_pdis: slpm_out_wkup_pdis { + ste,sleep = ; + ste,sleep-output = ; + ste,sleep-wakeup = ; + ste,sleep-pull-disable = ; + }; + + in_wkup_pdis: in_wkup_pdis { + ste,sleep-input = ; + ste,sleep-wakeup = ; + ste,sleep-pull-disable = ; + }; + + out_hi_wkup_pdis: out_hi_wkup_pdis { + ste,sleep-output = ; + ste,sleep-wakeup = ; + ste,sleep-pull-disable = ; + }; + + out_wkup_pdis: out_wkup_pdis { + ste,sleep-output = ; + ste,sleep-wakeup = ; + ste,sleep-pull-disable = ; + }; +}; diff --git a/include/dt-bindings/pinctrl/nomadik.h b/include/dt-bindings/pinctrl/nomadik.h new file mode 100644 index 000000000000..638fb321a1cb --- /dev/null +++ b/include/dt-bindings/pinctrl/nomadik.h @@ -0,0 +1,36 @@ +/* + * nomadik.h + * + * Copyright (C) ST-Ericsson SA 2013 + * Author: Gabriel Fernandez for ST-Ericsson. + * License terms: GNU General Public License (GPL), version 2 + */ + +#define INPUT_NOPULL 0 +#define INPUT_PULLUP 1 +#define INPUT_PULLDOWN 2 + +#define OUTPUT_LOW 0 +#define OUTPUT_HIGH 1 +#define DIR_OUTPUT 2 + +#define SLPM_DISABLED 0 +#define SLPM_ENABLED 1 + +#define SLPM_INPUT_NOPULL 0 +#define SLPM_INPUT_PULLUP 1 +#define SLPM_INPUT_PULLDOWN 2 +#define SLPM_DIR_INPUT 3 + +#define SLPM_OUTPUT_LOW 0 +#define SLPM_OUTPUT_HIGH 1 +#define SLPM_DIR_OUTPUT 2 + +#define SLPM_WAKEUP_DISABLE 0 +#define SLPM_WAKEUP_ENABLE 1 + +#define GPIOMODE_DISABLED 0 +#define GPIOMODE_ENABLED 1 + +#define SLPM_PDIS_DISABLED 0 +#define SLPM_PDIS_ENABLED 1 -- cgit v1.2.3-71-gd317 From 43387b37fa2d0f368142b8fa8c9440da92e5381b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 16 Jul 2013 09:12:04 +0200 Subject: drm/gem: create drm_gem_dumb_destroy All the gem based kms drivers really want the same function to destroy a dumb framebuffer backing storage object. So give it to them and roll it out in all drivers. This still leaves the option open for kms drivers which don't use GEM for backing storage, but it does decently simplify matters for gem drivers. Acked-by: Inki Dae Acked-by: Laurent Pinchart Cc: Intel Graphics Development Cc: Ben Skeggs Reviwed-by: Rob Clark Cc: Alex Deucher Acked-by: Patrik Jakobsson Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/ast/ast_drv.c | 2 +- drivers/gpu/drm/ast/ast_drv.h | 3 --- drivers/gpu/drm/ast/ast_main.c | 7 ------- drivers/gpu/drm/cirrus/cirrus_drv.c | 2 +- drivers/gpu/drm/cirrus/cirrus_drv.h | 3 --- drivers/gpu/drm/cirrus/cirrus_main.c | 7 ------- drivers/gpu/drm/drm_gem.c | 14 ++++++++++++++ drivers/gpu/drm/drm_gem_cma_helper.c | 10 ---------- drivers/gpu/drm/exynos/exynos_drm_drv.c | 2 +- drivers/gpu/drm/exynos/exynos_drm_gem.c | 20 -------------------- drivers/gpu/drm/exynos/exynos_drm_gem.h | 9 --------- drivers/gpu/drm/gma500/gem.c | 17 ----------------- drivers/gpu/drm/gma500/psb_drv.c | 2 +- drivers/gpu/drm/gma500/psb_drv.h | 2 -- drivers/gpu/drm/i915/i915_drv.c | 2 +- drivers/gpu/drm/i915/i915_drv.h | 2 -- drivers/gpu/drm/i915/i915_gem.c | 7 ------- drivers/gpu/drm/mgag200/mgag200_drv.c | 2 +- drivers/gpu/drm/mgag200/mgag200_drv.h | 3 --- drivers/gpu/drm/mgag200/mgag200_main.c | 7 ------- drivers/gpu/drm/nouveau/nouveau_display.c | 7 ------- drivers/gpu/drm/nouveau/nouveau_display.h | 2 -- drivers/gpu/drm/nouveau/nouveau_drm.c | 2 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/omapdrm/omap_drv.h | 2 -- drivers/gpu/drm/omapdrm/omap_gem.c | 15 --------------- drivers/gpu/drm/qxl/qxl_drv.c | 2 +- drivers/gpu/drm/qxl/qxl_drv.h | 3 --- drivers/gpu/drm/qxl/qxl_dumb.c | 7 ------- drivers/gpu/drm/radeon/radeon.h | 3 --- drivers/gpu/drm/radeon/radeon_drv.c | 5 +---- drivers/gpu/drm/radeon/radeon_gem.c | 7 ------- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 2 +- drivers/gpu/drm/shmobile/shmob_drm_drv.c | 2 +- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 2 +- drivers/gpu/drm/udl/udl_drv.c | 2 +- drivers/gpu/drm/udl/udl_drv.h | 2 -- drivers/gpu/drm/udl/udl_gem.c | 6 ------ drivers/gpu/host1x/drm/drm.c | 2 +- drivers/gpu/host1x/drm/gem.c | 6 ------ drivers/gpu/host1x/drm/gem.h | 2 -- drivers/staging/imx-drm/imx-drm-core.c | 2 +- include/drm/drmP.h | 3 +++ include/drm/drm_gem_cma_helper.h | 8 -------- 44 files changed, 33 insertions(+), 186 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index df0d0a08097a..a144fb044852 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -216,7 +216,7 @@ static struct drm_driver driver = { .gem_free_object = ast_gem_free_object, .dumb_create = ast_dumb_create, .dumb_map_offset = ast_dumb_mmap_offset, - .dumb_destroy = ast_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, }; diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 622d4ae7eb9e..796dbb212a41 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -322,9 +322,6 @@ ast_bo(struct ttm_buffer_object *bo) extern int ast_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args); -extern int ast_dumb_destroy(struct drm_file *file, - struct drm_device *dev, - uint32_t handle); extern int ast_gem_init_object(struct drm_gem_object *obj); extern void ast_gem_free_object(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index c195dc2abc09..7f6152d374ca 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -449,13 +449,6 @@ int ast_dumb_create(struct drm_file *file, return 0; } -int ast_dumb_destroy(struct drm_file *file, - struct drm_device *dev, - uint32_t handle) -{ - return drm_gem_handle_delete(file, handle); -} - int ast_gem_init_object(struct drm_gem_object *obj) { BUG(); diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index 8ecb601152ef..d35d99c15f84 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -102,7 +102,7 @@ static struct drm_driver driver = { .gem_free_object = cirrus_gem_free_object, .dumb_create = cirrus_dumb_create, .dumb_map_offset = cirrus_dumb_mmap_offset, - .dumb_destroy = cirrus_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, }; static struct pci_driver cirrus_pci_driver = { diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index bae55609e6c3..9b0bb9184afd 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -203,9 +203,6 @@ int cirrus_gem_create(struct drm_device *dev, int cirrus_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args); -int cirrus_dumb_destroy(struct drm_file *file, - struct drm_device *dev, - uint32_t handle); int cirrus_framebuffer_init(struct drm_device *dev, struct cirrus_framebuffer *gfb, diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index 3a7a0efe3675..f130a533a512 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -255,13 +255,6 @@ int cirrus_dumb_create(struct drm_file *file, return 0; } -int cirrus_dumb_destroy(struct drm_file *file, - struct drm_device *dev, - uint32_t handle) -{ - return drm_gem_handle_delete(file, handle); -} - int cirrus_gem_init_object(struct drm_gem_object *obj) { BUG(); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 1f7657286f04..9ab038c8dd5f 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -243,6 +243,20 @@ drm_gem_handle_delete(struct drm_file *filp, u32 handle) } EXPORT_SYMBOL(drm_gem_handle_delete); +/** + * drm_gem_dumb_destroy - dumb fb callback helper for gem based drivers + * + * This implements the ->dumb_destroy kms driver callback for drivers which use + * gem to manage their backing storage. + */ +int drm_gem_dumb_destroy(struct drm_file *file, + struct drm_device *dev, + uint32_t handle) +{ + return drm_gem_handle_delete(file, handle); +} +EXPORT_SYMBOL(drm_gem_dumb_destroy); + /** * Create a handle for this object. This adds a handle reference * to the object, which includes a regular reference count. Callers diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 847f09117666..0a4f80574eb4 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -281,16 +281,6 @@ int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma) } EXPORT_SYMBOL_GPL(drm_gem_cma_mmap); -/* - * drm_gem_cma_dumb_destroy - (struct drm_driver)->dumb_destroy callback function - */ -int drm_gem_cma_dumb_destroy(struct drm_file *file_priv, - struct drm_device *drm, unsigned int handle) -{ - return drm_gem_handle_delete(file_priv, handle); -} -EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_destroy); - #ifdef CONFIG_DEBUG_FS void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index ca2729a85129..21fc28ae5e6e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -271,7 +271,7 @@ static struct drm_driver exynos_drm_driver = { .gem_vm_ops = &exynos_drm_gem_vm_ops, .dumb_create = exynos_drm_gem_dumb_create, .dumb_map_offset = exynos_drm_gem_dumb_map_offset, - .dumb_destroy = exynos_drm_gem_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_export = exynos_dmabuf_prime_export, diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index be32db1ab290..b904633863e8 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -717,26 +717,6 @@ unlock: return ret; } -int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, - struct drm_device *dev, - unsigned int handle) -{ - int ret; - - /* - * obj->refcount and obj->handle_count are decreased and - * if both them are 0 then exynos_drm_gem_free_object() - * would be called by callback to release resources. - */ - ret = drm_gem_handle_delete(file_priv, handle); - if (ret < 0) { - DRM_ERROR("failed to delete drm_gem_handle.\n"); - return ret; - } - - return 0; -} - int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct drm_gem_object *obj = vma->vm_private_data; diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 468766bee450..09555afdfe9c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -151,15 +151,6 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle, uint64_t *offset); -/* - * destroy memory region allocated. - * - a gem handle and physical memory region pointed by a gem object - * would be released by drm_gem_handle_delete(). - */ -int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, - struct drm_device *dev, - unsigned int handle); - /* page fault handler and mmap fault address(virtual) to physical memory. */ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c index 2f77bea30b11..10ae8c52d06f 100644 --- a/drivers/gpu/drm/gma500/gem.c +++ b/drivers/gpu/drm/gma500/gem.c @@ -161,23 +161,6 @@ int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev, return psb_gem_create(file, dev, args->size, &args->handle); } -/** - * psb_gem_dumb_destroy - destroy a dumb buffer - * @file: client file - * @dev: our DRM device - * @handle: the object handle - * - * Destroy a handle that was created via psb_gem_dumb_create, at least - * we hope it was created that way. i915 seems to assume the caller - * does the checking but that might be worth review ! FIXME - */ -int psb_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, - uint32_t handle) -{ - /* No special work needed, drop the reference and see what falls out */ - return drm_gem_handle_delete(file, handle); -} - /** * psb_gem_fault - pagefault handler for GEM objects * @vma: the VMA of the GEM object diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index bddea5807442..ed06d5ce3757 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -652,7 +652,7 @@ static struct drm_driver driver = { .gem_vm_ops = &psb_gem_vm_ops, .dumb_create = psb_gem_dumb_create, .dumb_map_offset = psb_gem_dumb_map_gtt, - .dumb_destroy = psb_gem_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, .fops = &psb_gem_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 6053b8abcd12..984cacfcbaf2 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -838,8 +838,6 @@ extern int psb_gem_get_aperture(struct drm_device *dev, void *data, struct drm_file *file); extern int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args); -extern int psb_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, - uint32_t handle); extern int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev, uint32_t handle, uint64_t *offset); extern int psb_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index b07362f2675e..cca12db6dbb7 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1163,7 +1163,7 @@ static struct drm_driver driver = { .dumb_create = i915_gem_dumb_create, .dumb_map_offset = i915_gem_mmap_gtt, - .dumb_destroy = i915_gem_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, .ioctls = i915_ioctls, .fops = &i915_driver_fops, .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index cef35d3ab37b..5cb3e4d34230 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1732,8 +1732,6 @@ int i915_gem_dumb_create(struct drm_file *file_priv, struct drm_mode_create_dumb *args); int i915_gem_mmap_gtt(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle, uint64_t *offset); -int i915_gem_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev, - uint32_t handle); /** * Returns true if seq1 is later than seq2. */ diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 8673a000a373..2aa0894b59cd 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -247,13 +247,6 @@ i915_gem_dumb_create(struct drm_file *file, args->size, &args->handle); } -int i915_gem_dumb_destroy(struct drm_file *file, - struct drm_device *dev, - uint32_t handle) -{ - return drm_gem_handle_delete(file, handle); -} - /** * Creates a new mm object and returns a handle to it. */ diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 122b571ccc7c..bd9196478735 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -104,7 +104,7 @@ static struct drm_driver driver = { .gem_free_object = mgag200_gem_free_object, .dumb_create = mgag200_dumb_create, .dumb_map_offset = mgag200_dumb_mmap_offset, - .dumb_destroy = mgag200_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, }; static struct pci_driver mgag200_pci_driver = { diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index 12e2499d9352..baaae19332e2 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -264,9 +264,6 @@ int mgag200_gem_init_object(struct drm_gem_object *obj); int mgag200_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args); -int mgag200_dumb_destroy(struct drm_file *file, - struct drm_device *dev, - uint32_t handle); void mgag200_gem_free_object(struct drm_gem_object *obj); int mgag200_dumb_mmap_offset(struct drm_file *file, diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c index 1a75ea395b33..0f8b861b10b3 100644 --- a/drivers/gpu/drm/mgag200/mgag200_main.c +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -310,13 +310,6 @@ int mgag200_dumb_create(struct drm_file *file, return 0; } -int mgag200_dumb_destroy(struct drm_file *file, - struct drm_device *dev, - uint32_t handle) -{ - return drm_gem_handle_delete(file, handle); -} - int mgag200_gem_init_object(struct drm_gem_object *obj) { BUG(); diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 7a8caa126db6..c8ffba24720a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -680,13 +680,6 @@ nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev, return ret; } -int -nouveau_display_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev, - uint32_t handle) -{ - return drm_gem_handle_delete(file_priv, handle); -} - int nouveau_display_dumb_map_offset(struct drm_file *file_priv, struct drm_device *dev, diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index 1ea3e4734b62..185e74132a6d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h @@ -68,8 +68,6 @@ int nouveau_display_dumb_create(struct drm_file *, struct drm_device *, struct drm_mode_create_dumb *args); int nouveau_display_dumb_map_offset(struct drm_file *, struct drm_device *, u32 handle, u64 *offset); -int nouveau_display_dumb_destroy(struct drm_file *, struct drm_device *, - u32 handle); void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index e990327d117a..a900cde497a6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -715,7 +715,7 @@ driver = { .dumb_create = nouveau_display_dumb_create, .dumb_map_offset = nouveau_display_dumb_map_offset, - .dumb_destroy = nouveau_display_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index a3004f12b9a3..1ddd1a15764d 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -633,7 +633,7 @@ static struct drm_driver omap_drm_driver = { .gem_vm_ops = &omap_gem_vm_ops, .dumb_create = omap_gem_dumb_create, .dumb_map_offset = omap_gem_dumb_map_offset, - .dumb_destroy = omap_gem_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, .ioctls = ioctls, .num_ioctls = DRM_OMAP_NUM_IOCTLS, .fops = &omapdriver_fops, diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 14f17da2ce25..f2ba425d80dd 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -225,8 +225,6 @@ int omap_gem_init_object(struct drm_gem_object *obj); void *omap_gem_vaddr(struct drm_gem_object *obj); int omap_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, uint32_t handle, uint64_t *offset); -int omap_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, - uint32_t handle); int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args); int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma); diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index f90531fc00c9..b1f19702550f 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -628,21 +628,6 @@ int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev, OMAP_BO_SCANOUT | OMAP_BO_WC, &args->handle); } -/** - * omap_gem_dumb_destroy - destroy a dumb buffer - * @file: client file - * @dev: our DRM device - * @handle: the object handle - * - * Destroy a handle that was created via omap_gem_dumb_create. - */ -int omap_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, - uint32_t handle) -{ - /* No special work needed, drop the reference and see what falls out */ - return drm_gem_handle_delete(file, handle); -} - /** * omap_gem_dumb_map - buffer mapping for dumb interface * @file: our drm client file diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index df0b577a6608..48f2dfdeabcb 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -221,7 +221,7 @@ static struct drm_driver qxl_driver = { .dumb_create = qxl_mode_dumb_create, .dumb_map_offset = qxl_mode_dumb_mmap, - .dumb_destroy = qxl_mode_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, #if defined(CONFIG_DEBUG_FS) .debugfs_init = qxl_debugfs_init, .debugfs_cleanup = qxl_debugfs_takedown, diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index aacb791464a3..57cb7a8e6fb5 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -418,9 +418,6 @@ int qxl_bo_kmap(struct qxl_bo *bo, void **ptr); int qxl_mode_dumb_create(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args); -int qxl_mode_dumb_destroy(struct drm_file *file_priv, - struct drm_device *dev, - uint32_t handle); int qxl_mode_dumb_mmap(struct drm_file *filp, struct drm_device *dev, uint32_t handle, uint64_t *offset_p); diff --git a/drivers/gpu/drm/qxl/qxl_dumb.c b/drivers/gpu/drm/qxl/qxl_dumb.c index 847c4ee798f7..d34bb4130ff0 100644 --- a/drivers/gpu/drm/qxl/qxl_dumb.c +++ b/drivers/gpu/drm/qxl/qxl_dumb.c @@ -68,13 +68,6 @@ int qxl_mode_dumb_create(struct drm_file *file_priv, return 0; } -int qxl_mode_dumb_destroy(struct drm_file *file_priv, - struct drm_device *dev, - uint32_t handle) -{ - return drm_gem_handle_delete(file_priv, handle); -} - int qxl_mode_dumb_mmap(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle, uint64_t *offset_p) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 2f08219c39b6..19066d1dcb7d 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -492,9 +492,6 @@ int radeon_mode_dumb_create(struct drm_file *file_priv, int radeon_mode_dumb_mmap(struct drm_file *filp, struct drm_device *dev, uint32_t handle, uint64_t *offset_p); -int radeon_mode_dumb_destroy(struct drm_file *file_priv, - struct drm_device *dev, - uint32_t handle); /* * Semaphores. diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index cb7f1a8c5a4a..4071fe70c416 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -118,9 +118,6 @@ int radeon_mode_dumb_mmap(struct drm_file *filp, int radeon_mode_dumb_create(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args); -int radeon_mode_dumb_destroy(struct drm_file *file_priv, - struct drm_device *dev, - uint32_t handle); struct sg_table *radeon_gem_prime_get_sg_table(struct drm_gem_object *obj); struct drm_gem_object *radeon_gem_prime_import_sg_table(struct drm_device *dev, size_t size, @@ -421,7 +418,7 @@ static struct drm_driver kms_driver = { .gem_close_object = radeon_gem_object_close, .dumb_create = radeon_mode_dumb_create, .dumb_map_offset = radeon_mode_dumb_mmap, - .dumb_destroy = radeon_mode_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, .fops = &radeon_driver_kms_fops, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index aa796031ab65..dce99c8a5835 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -570,13 +570,6 @@ int radeon_mode_dumb_create(struct drm_file *file_priv, return 0; } -int radeon_mode_dumb_destroy(struct drm_file *file_priv, - struct drm_device *dev, - uint32_t handle) -{ - return drm_gem_handle_delete(file_priv, handle); -} - #if defined(CONFIG_DEBUG_FS) static int radeon_debugfs_gem_info(struct seq_file *m, void *data) { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index dc0fe09b2ba1..5069d9c234bb 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -258,7 +258,7 @@ static struct drm_driver rcar_du_driver = { .gem_prime_mmap = drm_gem_cma_prime_mmap, .dumb_create = rcar_du_dumb_create, .dumb_map_offset = drm_gem_cma_dumb_map_offset, - .dumb_destroy = drm_gem_cma_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, .fops = &rcar_du_fops, .name = "rcar-du", .desc = "Renesas R-Car Display Unit", diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index 5f83f9a3ef59..7f2ea1a5a45f 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -285,7 +285,7 @@ static struct drm_driver shmob_drm_driver = { .gem_prime_mmap = drm_gem_cma_prime_mmap, .dumb_create = drm_gem_cma_dumb_create, .dumb_map_offset = drm_gem_cma_dumb_map_offset, - .dumb_destroy = drm_gem_cma_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, .fops = &shmob_drm_fops, .name = "shmob-drm", .desc = "Renesas SH Mobile DRM", diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 40b71da5a214..14801c2235ae 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -519,7 +519,7 @@ static struct drm_driver tilcdc_driver = { .gem_vm_ops = &drm_gem_cma_vm_ops, .dumb_create = drm_gem_cma_dumb_create, .dumb_map_offset = drm_gem_cma_dumb_map_offset, - .dumb_destroy = drm_gem_cma_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, #ifdef CONFIG_DEBUG_FS .debugfs_init = tilcdc_debugfs_init, .debugfs_cleanup = tilcdc_debugfs_cleanup, diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index c0770dbba74a..bb0af58c769a 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -84,7 +84,7 @@ static struct drm_driver driver = { .dumb_create = udl_dumb_create, .dumb_map_offset = udl_gem_mmap, - .dumb_destroy = udl_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, .fops = &udl_driver_fops, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index cc6d90f28c71..56aec9409fa3 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -114,8 +114,6 @@ int udl_dumb_create(struct drm_file *file_priv, struct drm_mode_create_dumb *args); int udl_gem_mmap(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle, uint64_t *offset); -int udl_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev, - uint32_t handle); int udl_gem_init_object(struct drm_gem_object *obj); void udl_gem_free_object(struct drm_gem_object *gem_obj); diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index 2a4cb2f83b36..b5e3b8038253 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -66,12 +66,6 @@ int udl_dumb_create(struct drm_file *file, args->size, &args->handle); } -int udl_dumb_destroy(struct drm_file *file, struct drm_device *dev, - uint32_t handle) -{ - return drm_gem_handle_delete(file, handle); -} - int udl_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) { int ret; diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c index e184b00faacd..87aa09bf7ba8 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/host1x/drm/drm.c @@ -633,7 +633,7 @@ struct drm_driver tegra_drm_driver = { .gem_vm_ops = &tegra_bo_vm_ops, .dumb_create = tegra_bo_dumb_create, .dumb_map_offset = tegra_bo_dumb_map_offset, - .dumb_destroy = tegra_bo_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, .ioctls = tegra_drm_ioctls, .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls), diff --git a/drivers/gpu/host1x/drm/gem.c b/drivers/gpu/host1x/drm/gem.c index bc323b3dbe4d..3c35622c9f15 100644 --- a/drivers/gpu/host1x/drm/gem.c +++ b/drivers/gpu/host1x/drm/gem.c @@ -261,9 +261,3 @@ int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma) return ret; } - -int tegra_bo_dumb_destroy(struct drm_file *file, struct drm_device *drm, - unsigned int handle) -{ - return drm_gem_handle_delete(file, handle); -} diff --git a/drivers/gpu/host1x/drm/gem.h b/drivers/gpu/host1x/drm/gem.h index 34de2b486eb7..2e93b0379da8 100644 --- a/drivers/gpu/host1x/drm/gem.h +++ b/drivers/gpu/host1x/drm/gem.h @@ -49,8 +49,6 @@ int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm, struct drm_mode_create_dumb *args); int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm, uint32_t handle, uint64_t *offset); -int tegra_bo_dumb_destroy(struct drm_file *file, struct drm_device *drm, - unsigned int handle); int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma); diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index 9854a1daf606..a827858af944 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -797,7 +797,7 @@ static struct drm_driver imx_drm_driver = { .gem_vm_ops = &drm_gem_cma_vm_ops, .dumb_create = drm_gem_cma_dumb_create, .dumb_map_offset = drm_gem_cma_dumb_map_offset, - .dumb_destroy = drm_gem_cma_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, .get_vblank_counter = drm_vblank_count, .enable_vblank = imx_drm_enable_vblank, diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 4b518e05d293..a029dea13f48 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1548,6 +1548,9 @@ extern int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page ** extern struct sg_table *drm_prime_pages_to_sg(struct page **pages, int nr_pages); extern void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg); +int drm_gem_dumb_destroy(struct drm_file *file, + struct drm_device *dev, + uint32_t handle); void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv); void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h index c34f27f80bcc..89b4d7db1ebd 100644 --- a/include/drm/drm_gem_cma_helper.h +++ b/include/drm/drm_gem_cma_helper.h @@ -30,14 +30,6 @@ int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv, /* set vm_flags and we can change the vm attribute to other one at here. */ int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma); -/* - * destroy memory region allocated. - * - a gem handle and physical memory region pointed by a gem object - * would be released by drm_gem_handle_delete(). - */ -int drm_gem_cma_dumb_destroy(struct drm_file *file_priv, - struct drm_device *drm, unsigned int handle); - /* allocate physical memory. */ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, unsigned int size); -- cgit v1.2.3-71-gd317 From 7fc65eb731cda8304865669166fb9a4c519bee69 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 6 Aug 2013 09:59:46 +0100 Subject: drm: Apply kref_put_mutex() optimisations to drm_gem_object_unreference_unlocked() We can apply the same optimisation tricks as kref_put_mutex() in our local equivalent function. However, we have a different locking semantic (we unlock ourselves, in kref_put_mutex() the callee unlocks) so that we can use the same callbacks for both locked and unlocked kref_put()s and so can not simply convert to using kref_put_mutex() directly. Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- include/drm/drmP.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/drm/drmP.h b/include/drm/drmP.h index a029dea13f48..3b7fda557b8d 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1629,10 +1629,12 @@ drm_gem_object_unreference(struct drm_gem_object *obj) static inline void drm_gem_object_unreference_unlocked(struct drm_gem_object *obj) { - if (obj != NULL) { + if (obj && !atomic_add_unless(&obj->refcount.refcount, -1, 1)) { struct drm_device *dev = obj->dev; + mutex_lock(&dev->struct_mutex); - kref_put(&obj->refcount, drm_gem_object_free); + if (likely(atomic_dec_and_test(&obj->refcount.refcount))) + drm_gem_object_free(&obj->refcount); mutex_unlock(&dev->struct_mutex); } } -- cgit v1.2.3-71-gd317 From 31e5d7c67bd492fd0b2988440e21e31809c7c9af Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 27 Jul 2013 13:36:27 +0200 Subject: drm/mm: add "best_match" flag to drm_mm_insert_node() Add a "best_match" flag similar to the drm_mm_search_*() helpers so we can convert TTM to use them in follow up patches. We can also inline the non-generic helpers and move them into the header to allow compile-time optimizations. To make calls to drm_mm_{search,insert}_node() more readable, this converts the boolean argument to a flagset. There are pending patches that add additional flags for top-down allocators and more. v2: - use flag parameter instead of boolean "best_match" - convert *_search_free() helpers to also use flags argument Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_mm.c | 37 ++++++++--------------- drivers/gpu/drm/drm_vma_manager.c | 4 +-- drivers/gpu/drm/i915/i915_gem.c | 3 +- drivers/gpu/drm/i915/i915_gem_stolen.c | 12 +++++--- drivers/gpu/drm/sis/sis_mm.c | 6 ++-- drivers/gpu/drm/ttm/ttm_bo_manager.c | 3 +- drivers/gpu/drm/via/via_mm.c | 4 +-- include/drm/drm_mm.h | 54 ++++++++++++++++++++++------------ 8 files changed, 68 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index fe304f903b13..9a383272c8c7 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -212,12 +212,13 @@ EXPORT_SYMBOL(drm_mm_get_block_generic); */ int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, unsigned long size, unsigned alignment, - unsigned long color) + unsigned long color, + enum drm_mm_search_flags flags) { struct drm_mm_node *hole_node; hole_node = drm_mm_search_free_generic(mm, size, alignment, - color, 0); + color, flags); if (!hole_node) return -ENOSPC; @@ -226,13 +227,6 @@ int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, } EXPORT_SYMBOL(drm_mm_insert_node_generic); -int drm_mm_insert_node(struct drm_mm *mm, struct drm_mm_node *node, - unsigned long size, unsigned alignment) -{ - return drm_mm_insert_node_generic(mm, node, size, alignment, 0); -} -EXPORT_SYMBOL(drm_mm_insert_node); - static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, struct drm_mm_node *node, unsigned long size, unsigned alignment, @@ -313,13 +307,14 @@ EXPORT_SYMBOL(drm_mm_get_block_range_generic); */ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node, unsigned long size, unsigned alignment, unsigned long color, - unsigned long start, unsigned long end) + unsigned long start, unsigned long end, + enum drm_mm_search_flags flags) { struct drm_mm_node *hole_node; hole_node = drm_mm_search_free_in_range_generic(mm, size, alignment, color, - start, end, 0); + start, end, flags); if (!hole_node) return -ENOSPC; @@ -330,14 +325,6 @@ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *n } EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic); -int drm_mm_insert_node_in_range(struct drm_mm *mm, struct drm_mm_node *node, - unsigned long size, unsigned alignment, - unsigned long start, unsigned long end) -{ - return drm_mm_insert_node_in_range_generic(mm, node, size, alignment, 0, start, end); -} -EXPORT_SYMBOL(drm_mm_insert_node_in_range); - /** * Remove a memory node from the allocator. */ @@ -413,7 +400,7 @@ struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, unsigned long size, unsigned alignment, unsigned long color, - bool best_match) + enum drm_mm_search_flags flags) { struct drm_mm_node *entry; struct drm_mm_node *best; @@ -436,7 +423,7 @@ struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, if (!check_free_hole(adj_start, adj_end, size, alignment)) continue; - if (!best_match) + if (!(flags & DRM_MM_SEARCH_BEST)) return entry; if (entry->size < best_size) { @@ -455,7 +442,7 @@ struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, unsigned long color, unsigned long start, unsigned long end, - bool best_match) + enum drm_mm_search_flags flags) { struct drm_mm_node *entry; struct drm_mm_node *best; @@ -483,7 +470,7 @@ struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, if (!check_free_hole(adj_start, adj_end, size, alignment)) continue; - if (!best_match) + if (!(flags & DRM_MM_SEARCH_BEST)) return entry; if (entry->size < best_size) { @@ -629,8 +616,8 @@ EXPORT_SYMBOL(drm_mm_scan_add_block); * corrupted. * * When the scan list is empty, the selected memory nodes can be freed. An - * immediately following drm_mm_search_free with best_match = 0 will then return - * the just freed block (because its at the top of the free_stack list). + * immediately following drm_mm_search_free with !DRM_MM_SEARCH_BEST will then + * return the just freed block (because its at the top of the free_stack list). * * Returns one if this block should be evicted, zero otherwise. Will always * return zero when no hole has been found. diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c index b966cea95f11..3837481d5607 100644 --- a/drivers/gpu/drm/drm_vma_manager.c +++ b/drivers/gpu/drm/drm_vma_manager.c @@ -241,8 +241,8 @@ int drm_vma_offset_add(struct drm_vma_offset_manager *mgr, goto out_unlock; } - ret = drm_mm_insert_node_generic(&mgr->vm_addr_space_mm, - &node->vm_node, pages, 0, 0); + ret = drm_mm_insert_node(&mgr->vm_addr_space_mm, &node->vm_node, + pages, 0, DRM_MM_SEARCH_DEFAULT); if (ret) goto out_unlock; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 2aa0894b59cd..ea2d83d7324e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3085,7 +3085,8 @@ search_free: ret = drm_mm_insert_node_in_range_generic(&dev_priv->mm.gtt_space, &obj->gtt_space, size, alignment, - obj->cache_level, 0, gtt_max); + obj->cache_level, 0, gtt_max, + DRM_MM_SEARCH_DEFAULT); if (ret) { ret = i915_gem_evict_something(dev, size, alignment, obj->cache_level, diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index 55218332e625..e3551706f4ff 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -115,10 +115,12 @@ static int i915_setup_compression(struct drm_device *dev, int size) /* Try to over-allocate to reduce reallocations and fragmentation */ compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, - size <<= 1, 4096, 0); + size <<= 1, 4096, + DRM_MM_SEARCH_DEFAULT); if (!compressed_fb) compressed_fb = drm_mm_search_free(&dev_priv->mm.stolen, - size >>= 1, 4096, 0); + size >>= 1, 4096, + DRM_MM_SEARCH_DEFAULT); if (compressed_fb) compressed_fb = drm_mm_get_block(compressed_fb, size, 4096); if (!compressed_fb) @@ -130,7 +132,8 @@ static int i915_setup_compression(struct drm_device *dev, int size) I915_WRITE(DPFC_CB_BASE, compressed_fb->start); } else { compressed_llb = drm_mm_search_free(&dev_priv->mm.stolen, - 4096, 4096, 0); + 4096, 4096, + DRM_MM_SEARCH_DEFAULT); if (compressed_llb) compressed_llb = drm_mm_get_block(compressed_llb, 4096, 4096); @@ -328,7 +331,8 @@ i915_gem_object_create_stolen(struct drm_device *dev, u32 size) if (size == 0) return NULL; - stolen = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, 0); + stolen = drm_mm_search_free(&dev_priv->mm.stolen, size, 4096, + DRM_MM_SEARCH_DEFAULT); if (stolen) stolen = drm_mm_get_block(stolen, size, 4096); if (stolen == NULL) diff --git a/drivers/gpu/drm/sis/sis_mm.c b/drivers/gpu/drm/sis/sis_mm.c index 9a43d98e5003..23a234985941 100644 --- a/drivers/gpu/drm/sis/sis_mm.c +++ b/drivers/gpu/drm/sis/sis_mm.c @@ -109,7 +109,8 @@ static int sis_drm_alloc(struct drm_device *dev, struct drm_file *file, if (pool == AGP_TYPE) { retval = drm_mm_insert_node(&dev_priv->agp_mm, &item->mm_node, - mem->size, 0); + mem->size, 0, + DRM_MM_SEARCH_DEFAULT); offset = item->mm_node.start; } else { #if defined(CONFIG_FB_SIS) || defined(CONFIG_FB_SIS_MODULE) @@ -121,7 +122,8 @@ static int sis_drm_alloc(struct drm_device *dev, struct drm_file *file, #else retval = drm_mm_insert_node(&dev_priv->vram_mm, &item->mm_node, - mem->size, 0); + mem->size, 0, + DRM_MM_SEARCH_DEFAULT); offset = item->mm_node.start; #endif } diff --git a/drivers/gpu/drm/ttm/ttm_bo_manager.c b/drivers/gpu/drm/ttm/ttm_bo_manager.c index e4367f91472a..e4be29efba6b 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_manager.c +++ b/drivers/gpu/drm/ttm/ttm_bo_manager.c @@ -69,7 +69,8 @@ static int ttm_bo_man_get_node(struct ttm_mem_type_manager *man, spin_lock(&rman->lock); node = drm_mm_search_free_in_range(mm, mem->num_pages, mem->page_alignment, - placement->fpfn, lpfn, 1); + placement->fpfn, lpfn, + DRM_MM_SEARCH_BEST); if (unlikely(node == NULL)) { spin_unlock(&rman->lock); return 0; diff --git a/drivers/gpu/drm/via/via_mm.c b/drivers/gpu/drm/via/via_mm.c index 0ab93ff09873..7e3ad87c366c 100644 --- a/drivers/gpu/drm/via/via_mm.c +++ b/drivers/gpu/drm/via/via_mm.c @@ -140,11 +140,11 @@ int via_mem_alloc(struct drm_device *dev, void *data, if (mem->type == VIA_MEM_AGP) retval = drm_mm_insert_node(&dev_priv->agp_mm, &item->mm_node, - tmpSize, 0); + tmpSize, 0, DRM_MM_SEARCH_DEFAULT); else retval = drm_mm_insert_node(&dev_priv->vram_mm, &item->mm_node, - tmpSize, 0); + tmpSize, 0, DRM_MM_SEARCH_DEFAULT); if (retval) goto fail_alloc; diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 98cb50ea6acb..439d1a17d3b1 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -44,6 +44,11 @@ #include #endif +enum drm_mm_search_flags { + DRM_MM_SEARCH_DEFAULT = 0, + DRM_MM_SEARCH_BEST = 1 << 0, +}; + struct drm_mm_node { struct list_head node_list; struct list_head hole_stack; @@ -189,28 +194,41 @@ static inline struct drm_mm_node *drm_mm_get_block_atomic_range( start, end, 1); } -extern int drm_mm_insert_node(struct drm_mm *mm, - struct drm_mm_node *node, - unsigned long size, - unsigned alignment); -extern int drm_mm_insert_node_in_range(struct drm_mm *mm, - struct drm_mm_node *node, - unsigned long size, - unsigned alignment, - unsigned long start, - unsigned long end); extern int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, unsigned long size, unsigned alignment, - unsigned long color); + unsigned long color, + enum drm_mm_search_flags flags); +static inline int drm_mm_insert_node(struct drm_mm *mm, + struct drm_mm_node *node, + unsigned long size, + unsigned alignment, + enum drm_mm_search_flags flags) +{ + return drm_mm_insert_node_generic(mm, node, size, alignment, 0, flags); +} + extern int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node, unsigned long size, unsigned alignment, unsigned long color, unsigned long start, - unsigned long end); + unsigned long end, + enum drm_mm_search_flags flags); +static inline int drm_mm_insert_node_in_range(struct drm_mm *mm, + struct drm_mm_node *node, + unsigned long size, + unsigned alignment, + unsigned long start, + unsigned long end, + enum drm_mm_search_flags flags) +{ + return drm_mm_insert_node_in_range_generic(mm, node, size, alignment, + 0, start, end, flags); +} + extern void drm_mm_put_block(struct drm_mm_node *cur); extern void drm_mm_remove_node(struct drm_mm_node *node); extern void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new); @@ -218,7 +236,7 @@ extern struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, unsigned long size, unsigned alignment, unsigned long color, - bool best_match); + enum drm_mm_search_flags flags); extern struct drm_mm_node *drm_mm_search_free_in_range_generic( const struct drm_mm *mm, unsigned long size, @@ -226,13 +244,13 @@ extern struct drm_mm_node *drm_mm_search_free_in_range_generic( unsigned long color, unsigned long start, unsigned long end, - bool best_match); + enum drm_mm_search_flags flags); static inline struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm, unsigned long size, unsigned alignment, - bool best_match) + enum drm_mm_search_flags flags) { - return drm_mm_search_free_generic(mm,size, alignment, 0, best_match); + return drm_mm_search_free_generic(mm,size, alignment, 0, flags); } static inline struct drm_mm_node *drm_mm_search_free_in_range( const struct drm_mm *mm, @@ -240,10 +258,10 @@ static inline struct drm_mm_node *drm_mm_search_free_in_range( unsigned alignment, unsigned long start, unsigned long end, - bool best_match) + enum drm_mm_search_flags flags) { return drm_mm_search_free_in_range_generic(mm, size, alignment, 0, - start, end, best_match); + start, end, flags); } extern void drm_mm_init(struct drm_mm *mm, -- cgit v1.2.3-71-gd317 From baa7094355a10b432bbccacb925da4bdac861c8d Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 2 Aug 2013 13:27:49 -0400 Subject: drm: const'ify ioctls table (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because, there is no reason for it not to be const. v1: original v2: fix compile break in vmwgfx, and couple related cleanups suggested by Ville Syrjälä Signed-off-by: Rob Clark Reviewed-by: Ville Syrjälä Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/exynos/exynos_drm_drv.c | 4 ++-- drivers/gpu/drm/gma500/psb_drv.c | 2 +- drivers/gpu/drm/i810/i810_dma.c | 2 +- drivers/gpu/drm/i810/i810_drv.h | 2 +- drivers/gpu/drm/i915/i915_dma.c | 2 +- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/mga/mga_drv.h | 2 +- drivers/gpu/drm/mga/mga_state.c | 2 +- drivers/gpu/drm/nouveau/nouveau_drm.c | 5 ++--- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/qxl/qxl_drv.h | 2 +- drivers/gpu/drm/qxl/qxl_ioctl.c | 2 +- drivers/gpu/drm/r128/r128_drv.h | 2 +- drivers/gpu/drm/r128/r128_state.c | 2 +- drivers/gpu/drm/radeon/radeon_drv.c | 2 +- drivers/gpu/drm/radeon/radeon_kms.c | 2 +- drivers/gpu/drm/savage/savage_bci.c | 2 +- drivers/gpu/drm/savage/savage_drv.h | 2 +- drivers/gpu/drm/sis/sis_drv.h | 2 +- drivers/gpu/drm/sis/sis_mm.c | 2 +- drivers/gpu/drm/via/via_dma.c | 2 +- drivers/gpu/drm/via/via_drv.h | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 4 ++-- drivers/gpu/host1x/drm/drm.c | 2 +- drivers/staging/imx-drm/imx-drm-core.c | 2 +- include/drm/drmP.h | 2 +- 26 files changed, 29 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 21fc28ae5e6e..df81d3c959b4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -213,7 +213,7 @@ static const struct vm_operations_struct exynos_drm_gem_vm_ops = { .close = drm_gem_vm_close, }; -static struct drm_ioctl_desc exynos_ioctls[] = { +static const struct drm_ioctl_desc exynos_ioctls[] = { DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl, DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP_OFFSET, @@ -277,6 +277,7 @@ static struct drm_driver exynos_drm_driver = { .gem_prime_export = exynos_dmabuf_prime_export, .gem_prime_import = exynos_dmabuf_prime_import, .ioctls = exynos_ioctls, + .num_ioctls = ARRAY_SIZE(exynos_ioctls), .fops = &exynos_drm_driver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -288,7 +289,6 @@ static struct drm_driver exynos_drm_driver = { static int exynos_drm_platform_probe(struct platform_device *pdev) { pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls); return drm_platform_init(&exynos_drm_driver, pdev); } diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index ed06d5ce3757..d13c2fc848bc 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -131,7 +131,7 @@ static int psb_gamma_ioctl(struct drm_device *dev, void *data, static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -static struct drm_ioctl_desc psb_ioctls[] = { +static const struct drm_ioctl_desc psb_ioctls[] = { DRM_IOCTL_DEF_DRV(GMA_ADB, psb_adb_ioctl, DRM_AUTH), DRM_IOCTL_DEF_DRV(GMA_MODE_OPERATION, psb_mode_operation_ioctl, DRM_AUTH), diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c index ada49eda489f..eac755bb8f9b 100644 --- a/drivers/gpu/drm/i810/i810_dma.c +++ b/drivers/gpu/drm/i810/i810_dma.c @@ -1241,7 +1241,7 @@ int i810_driver_dma_quiescent(struct drm_device *dev) return 0; } -struct drm_ioctl_desc i810_ioctls[] = { +const struct drm_ioctl_desc i810_ioctls[] = { DRM_IOCTL_DEF_DRV(I810_INIT, i810_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I810_VERTEX, i810_dma_vertex, DRM_AUTH|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I810_CLEAR, i810_clear_bufs, DRM_AUTH|DRM_UNLOCKED), diff --git a/drivers/gpu/drm/i810/i810_drv.h b/drivers/gpu/drm/i810/i810_drv.h index 6e0acad9e0f5..d4d16eddd651 100644 --- a/drivers/gpu/drm/i810/i810_drv.h +++ b/drivers/gpu/drm/i810/i810_drv.h @@ -125,7 +125,7 @@ extern void i810_driver_preclose(struct drm_device *dev, extern int i810_driver_device_is_agp(struct drm_device *dev); extern long i810_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -extern struct drm_ioctl_desc i810_ioctls[]; +extern const struct drm_ioctl_desc i810_ioctls[]; extern int i810_max_ioctl; #define I810_BASE(reg) ((unsigned long) \ diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 6ce903306320..78ad4dcc8e2f 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1834,7 +1834,7 @@ void i915_driver_postclose(struct drm_device *dev, struct drm_file *file) kfree(file_priv); } -struct drm_ioctl_desc i915_ioctls[] = { +const struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_INIT, i915_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(I915_FLUSH, i915_flush_ioctl, DRM_AUTH), DRM_IOCTL_DEF_DRV(I915_FLIP, i915_flip_bufs, DRM_AUTH), diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 5cb3e4d34230..3dd5731769fa 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1566,7 +1566,7 @@ struct drm_i915_file_private { #define INTEL_RC6p_ENABLE (1<<1) #define INTEL_RC6pp_ENABLE (1<<2) -extern struct drm_ioctl_desc i915_ioctls[]; +extern const struct drm_ioctl_desc i915_ioctls[]; extern int i915_max_ioctl; extern unsigned int i915_fbpercrtc __always_unused; extern int i915_panel_ignore_lid __read_mostly; diff --git a/drivers/gpu/drm/mga/mga_drv.h b/drivers/gpu/drm/mga/mga_drv.h index 54558a01969a..ca4bc54ea214 100644 --- a/drivers/gpu/drm/mga/mga_drv.h +++ b/drivers/gpu/drm/mga/mga_drv.h @@ -149,7 +149,7 @@ typedef struct drm_mga_private { unsigned int agp_size; } drm_mga_private_t; -extern struct drm_ioctl_desc mga_ioctls[]; +extern const struct drm_ioctl_desc mga_ioctls[]; extern int mga_max_ioctl; /* mga_dma.c */ diff --git a/drivers/gpu/drm/mga/mga_state.c b/drivers/gpu/drm/mga/mga_state.c index 9c145143ad0f..37cc2fb4eadd 100644 --- a/drivers/gpu/drm/mga/mga_state.c +++ b/drivers/gpu/drm/mga/mga_state.c @@ -1083,7 +1083,7 @@ file_priv) return 0; } -struct drm_ioctl_desc mga_ioctls[] = { +const struct drm_ioctl_desc mga_ioctls[] = { DRM_IOCTL_DEF_DRV(MGA_INIT, mga_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(MGA_FLUSH, mga_dma_flush, DRM_AUTH), DRM_IOCTL_DEF_DRV(MGA_RESET, mga_dma_reset, DRM_AUTH), diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index a900cde497a6..2c2097af2378 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -640,7 +640,7 @@ nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv) nouveau_cli_destroy(cli); } -static struct drm_ioctl_desc +static const struct drm_ioctl_desc nouveau_ioctls[] = { DRM_IOCTL_DEF_DRV(NOUVEAU_GETPARAM, nouveau_abi16_ioctl_getparam, DRM_UNLOCKED|DRM_AUTH), DRM_IOCTL_DEF_DRV(NOUVEAU_SETPARAM, nouveau_abi16_ioctl_setparam, DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), @@ -695,6 +695,7 @@ driver = { .disable_vblank = nouveau_drm_vblank_disable, .ioctls = nouveau_ioctls, + .num_ioctls = ARRAY_SIZE(nouveau_ioctls), .fops = &nouveau_driver_fops, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, @@ -765,8 +766,6 @@ nouveau_drm_pci_driver = { static int __init nouveau_drm_init(void) { - driver.num_ioctls = ARRAY_SIZE(nouveau_ioctls); - if (nouveau_modeset == -1) { #ifdef CONFIG_VGA_CONSOLE if (vgacon_text_force()) diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 1ddd1a15764d..2f9e22e22bd4 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -419,7 +419,7 @@ static int ioctl_gem_info(struct drm_device *dev, void *data, return ret; } -static struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = { +static const struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = { DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param, DRM_UNLOCKED|DRM_AUTH), DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, ioctl_set_param, DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new, DRM_UNLOCKED|DRM_AUTH), diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 57cb7a8e6fb5..afd09d48d72c 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -319,7 +319,7 @@ struct qxl_device { /* forward declaration for QXL_INFO_IO */ void qxl_io_log(struct qxl_device *qdev, const char *fmt, ...); -extern struct drm_ioctl_desc qxl_ioctls[]; +extern const struct drm_ioctl_desc qxl_ioctls[]; extern int qxl_max_ioctl; int qxl_driver_load(struct drm_device *dev, unsigned long flags); diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c index 27f45e49250d..6cd7273c0804 100644 --- a/drivers/gpu/drm/qxl/qxl_ioctl.c +++ b/drivers/gpu/drm/qxl/qxl_ioctl.c @@ -402,7 +402,7 @@ static int qxl_alloc_surf_ioctl(struct drm_device *dev, void *data, return ret; } -struct drm_ioctl_desc qxl_ioctls[] = { +const struct drm_ioctl_desc qxl_ioctls[] = { DRM_IOCTL_DEF_DRV(QXL_ALLOC, qxl_alloc_ioctl, DRM_AUTH|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(QXL_MAP, qxl_map_ioctl, DRM_AUTH|DRM_UNLOCKED), diff --git a/drivers/gpu/drm/r128/r128_drv.h b/drivers/gpu/drm/r128/r128_drv.h index 930c71b2fb5e..56eb5e3f5439 100644 --- a/drivers/gpu/drm/r128/r128_drv.h +++ b/drivers/gpu/drm/r128/r128_drv.h @@ -131,7 +131,7 @@ typedef struct drm_r128_buf_priv { drm_r128_freelist_t *list_entry; } drm_r128_buf_priv_t; -extern struct drm_ioctl_desc r128_ioctls[]; +extern const struct drm_ioctl_desc r128_ioctls[]; extern int r128_max_ioctl; /* r128_cce.c */ diff --git a/drivers/gpu/drm/r128/r128_state.c b/drivers/gpu/drm/r128/r128_state.c index 19bb7e6f3d9a..01dd9aef9f0e 100644 --- a/drivers/gpu/drm/r128/r128_state.c +++ b/drivers/gpu/drm/r128/r128_state.c @@ -1643,7 +1643,7 @@ void r128_driver_lastclose(struct drm_device *dev) r128_do_cleanup_cce(dev); } -struct drm_ioctl_desc r128_ioctls[] = { +const struct drm_ioctl_desc r128_ioctls[] = { DRM_IOCTL_DEF_DRV(R128_INIT, r128_cce_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(R128_CCE_START, r128_cce_start, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(R128_CCE_STOP, r128_cce_stop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 4071fe70c416..fa7a7e13da6c 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -109,7 +109,7 @@ void radeon_gem_object_close(struct drm_gem_object *obj, struct drm_file *file_priv); extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, int *vpos, int *hpos); -extern struct drm_ioctl_desc radeon_ioctls_kms[]; +extern const struct drm_ioctl_desc radeon_ioctls_kms[]; extern int radeon_max_kms_ioctl; int radeon_mmap(struct file *filp, struct vm_area_struct *vma); int radeon_mode_dumb_mmap(struct drm_file *filp, diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 07b023655bb4..866c2b70aa6f 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -722,7 +722,7 @@ KMS_INVALID_IOCTL(radeon_surface_alloc_kms) KMS_INVALID_IOCTL(radeon_surface_free_kms) -struct drm_ioctl_desc radeon_ioctls_kms[] = { +const struct drm_ioctl_desc radeon_ioctls_kms[] = { DRM_IOCTL_DEF_DRV(RADEON_CP_INIT, radeon_cp_init_kms, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(RADEON_CP_START, radeon_cp_start_kms, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(RADEON_CP_STOP, radeon_cp_stop_kms, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), diff --git a/drivers/gpu/drm/savage/savage_bci.c b/drivers/gpu/drm/savage/savage_bci.c index bd6b2cf508d5..b17d0710871a 100644 --- a/drivers/gpu/drm/savage/savage_bci.c +++ b/drivers/gpu/drm/savage/savage_bci.c @@ -1072,7 +1072,7 @@ void savage_reclaim_buffers(struct drm_device *dev, struct drm_file *file_priv) drm_idlelock_release(&file_priv->master->lock); } -struct drm_ioctl_desc savage_ioctls[] = { +const struct drm_ioctl_desc savage_ioctls[] = { DRM_IOCTL_DEF_DRV(SAVAGE_BCI_INIT, savage_bci_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF_DRV(SAVAGE_BCI_CMDBUF, savage_bci_cmdbuf, DRM_AUTH), DRM_IOCTL_DEF_DRV(SAVAGE_BCI_EVENT_EMIT, savage_bci_event_emit, DRM_AUTH), diff --git a/drivers/gpu/drm/savage/savage_drv.h b/drivers/gpu/drm/savage/savage_drv.h index c05082a59f6f..335f8fcf1041 100644 --- a/drivers/gpu/drm/savage/savage_drv.h +++ b/drivers/gpu/drm/savage/savage_drv.h @@ -104,7 +104,7 @@ enum savage_family { S3_LAST }; -extern struct drm_ioctl_desc savage_ioctls[]; +extern const struct drm_ioctl_desc savage_ioctls[]; extern int savage_max_ioctl; #define S3_SAVAGE3D_SERIES(chip) ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE_MX)) diff --git a/drivers/gpu/drm/sis/sis_drv.h b/drivers/gpu/drm/sis/sis_drv.h index 13b527bb83be..c31c0253054d 100644 --- a/drivers/gpu/drm/sis/sis_drv.h +++ b/drivers/gpu/drm/sis/sis_drv.h @@ -70,7 +70,7 @@ extern void sis_reclaim_buffers_locked(struct drm_device *dev, struct drm_file *file_priv); extern void sis_lastclose(struct drm_device *dev); -extern struct drm_ioctl_desc sis_ioctls[]; +extern const struct drm_ioctl_desc sis_ioctls[]; extern int sis_max_ioctl; #endif diff --git a/drivers/gpu/drm/sis/sis_mm.c b/drivers/gpu/drm/sis/sis_mm.c index 23a234985941..01857d836350 100644 --- a/drivers/gpu/drm/sis/sis_mm.c +++ b/drivers/gpu/drm/sis/sis_mm.c @@ -350,7 +350,7 @@ void sis_reclaim_buffers_locked(struct drm_device *dev, return; } -struct drm_ioctl_desc sis_ioctls[] = { +const struct drm_ioctl_desc sis_ioctls[] = { DRM_IOCTL_DEF_DRV(SIS_FB_ALLOC, sis_fb_alloc, DRM_AUTH), DRM_IOCTL_DEF_DRV(SIS_FB_FREE, sis_drm_free, DRM_AUTH), DRM_IOCTL_DEF_DRV(SIS_AGP_INIT, sis_ioctl_agp_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY), diff --git a/drivers/gpu/drm/via/via_dma.c b/drivers/gpu/drm/via/via_dma.c index 13558f5a2422..652f9b43ec9d 100644 --- a/drivers/gpu/drm/via/via_dma.c +++ b/drivers/gpu/drm/via/via_dma.c @@ -720,7 +720,7 @@ static int via_cmdbuf_size(struct drm_device *dev, void *data, struct drm_file * return ret; } -struct drm_ioctl_desc via_ioctls[] = { +const struct drm_ioctl_desc via_ioctls[] = { DRM_IOCTL_DEF_DRV(VIA_ALLOCMEM, via_mem_alloc, DRM_AUTH), DRM_IOCTL_DEF_DRV(VIA_FREEMEM, via_mem_free, DRM_AUTH), DRM_IOCTL_DEF_DRV(VIA_AGP_INIT, via_agp_init, DRM_AUTH|DRM_MASTER), diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h index 893a65090c36..a811ef2b505f 100644 --- a/drivers/gpu/drm/via/via_drv.h +++ b/drivers/gpu/drm/via/via_drv.h @@ -114,7 +114,7 @@ enum via_family { #define VIA_READ8(reg) DRM_READ8(VIA_BASE, reg) #define VIA_WRITE8(reg, val) DRM_WRITE8(VIA_BASE, reg, val) -extern struct drm_ioctl_desc via_ioctls[]; +extern const struct drm_ioctl_desc via_ioctls[]; extern int via_max_ioctl; extern int via_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 78e21649d48a..50861504b5d9 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -124,7 +124,7 @@ * Ioctl definitions. */ -static struct drm_ioctl_desc vmw_ioctls[] = { +static const struct drm_ioctl_desc vmw_ioctls[] = { VMW_IOCTL_DEF(VMW_GET_PARAM, vmw_getparam_ioctl, DRM_AUTH | DRM_UNLOCKED), VMW_IOCTL_DEF(VMW_ALLOC_DMABUF, vmw_dmabuf_alloc_ioctl, @@ -782,7 +782,7 @@ static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd, if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END) && (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) { - struct drm_ioctl_desc *ioctl = + const struct drm_ioctl_desc *ioctl = &vmw_ioctls[nr - DRM_COMMAND_BASE]; if (unlikely(ioctl->cmd_drv != cmd)) { diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c index 87aa09bf7ba8..b128b90a94f6 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/host1x/drm/drm.c @@ -487,7 +487,7 @@ static int tegra_submit(struct drm_device *drm, void *data, } #endif -static struct drm_ioctl_desc tegra_drm_ioctls[] = { +static const struct drm_ioctl_desc tegra_drm_ioctls[] = { #ifdef CONFIG_DRM_TEGRA_STAGING DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_UNLOCKED | DRM_AUTH), DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_UNLOCKED), diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index a827858af944..a8900496b980 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -783,7 +783,7 @@ int imx_drm_remove_connector(struct imx_drm_connector *imx_drm_connector) } EXPORT_SYMBOL_GPL(imx_drm_remove_connector); -static struct drm_ioctl_desc imx_drm_ioctls[] = { +static const struct drm_ioctl_desc imx_drm_ioctls[] = { /* none so far */ }; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 3b7fda557b8d..1a4eba627e79 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -965,7 +965,7 @@ struct drm_driver { u32 driver_features; int dev_priv_size; - struct drm_ioctl_desc *ioctls; + const struct drm_ioctl_desc *ioctls; int num_ioctls; const struct file_operations *fops; union { -- cgit v1.2.3-71-gd317 From 28ec711cd427f8b61f73712a43b8100ba8ca933b Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 27 Jul 2013 16:37:00 +0200 Subject: drm/agp: move AGP cleanup paths to drm_agpsupport.c Introduce two new helpers, drm_agp_clear() and drm_agp_destroy() which clear all AGP mappings and destroy the AGP head. This allows to reduce the AGP code in core DRM and move it all to drm_agpsupport.c. Signed-off-by: David Herrmann Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_agpsupport.c | 51 ++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_drv.c | 21 +---------------- drivers/gpu/drm/drm_pci.c | 12 ++++++++++ drivers/gpu/drm/drm_stub.c | 9 ++----- include/drm/drmP.h | 3 +++ 5 files changed, 69 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_agpsupport.c b/drivers/gpu/drm/drm_agpsupport.c index 3d8fed179797..e301d653d97e 100644 --- a/drivers/gpu/drm/drm_agpsupport.c +++ b/drivers/gpu/drm/drm_agpsupport.c @@ -423,6 +423,57 @@ struct drm_agp_head *drm_agp_init(struct drm_device *dev) return head; } +/** + * drm_agp_clear - Clear AGP resource list + * @dev: DRM device + * + * Iterate over all AGP resources and remove them. But keep the AGP head + * intact so it can still be used. It is safe to call this if AGP is disabled or + * was already removed. + * + * If DRIVER_MODESET is active, nothing is done to protect the modesetting + * resources from getting destroyed. Drivers are responsible of cleaning them up + * during device shutdown. + */ +void drm_agp_clear(struct drm_device *dev) +{ + struct drm_agp_mem *entry, *tempe; + + if (!drm_core_has_AGP(dev) || !dev->agp) + return; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + list_for_each_entry_safe(entry, tempe, &dev->agp->memory, head) { + if (entry->bound) + drm_unbind_agp(entry->memory); + drm_free_agp(entry->memory, entry->pages); + kfree(entry); + } + INIT_LIST_HEAD(&dev->agp->memory); + + if (dev->agp->acquired) + drm_agp_release(dev); + + dev->agp->acquired = 0; + dev->agp->enabled = 0; +} + +/** + * drm_agp_destroy - Destroy AGP head + * @dev: DRM device + * + * Destroy resources that were previously allocated via drm_agp_initp. Caller + * must ensure to clean up all AGP resources before calling this. See + * drm_agp_clear(). + * + * Call this to destroy AGP heads allocated via drm_agp_init(). + */ +void drm_agp_destroy(struct drm_agp_head *agp) +{ + kfree(agp); +} + /** * Binds a collection of pages into AGP memory at the given offset, returning * the AGP memory structure containing them. diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 36103d1660d1..dddd79988ffc 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -195,27 +195,8 @@ int drm_lastclose(struct drm_device * dev) mutex_lock(&dev->struct_mutex); - /* Clear AGP information */ - if (drm_core_has_AGP(dev) && dev->agp && - !drm_core_check_feature(dev, DRIVER_MODESET)) { - struct drm_agp_mem *entry, *tempe; - - /* Remove AGP resources, but leave dev->agp - intact until drv_cleanup is called. */ - list_for_each_entry_safe(entry, tempe, &dev->agp->memory, head) { - if (entry->bound) - drm_unbind_agp(entry->memory); - drm_free_agp(entry->memory, entry->pages); - kfree(entry); - } - INIT_LIST_HEAD(&dev->agp->memory); - - if (dev->agp->acquired) - drm_agp_release(dev); + drm_agp_clear(dev); - dev->agp->acquired = 0; - dev->agp->enabled = 0; - } if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg && !drm_core_check_feature(dev, DRIVER_MODESET)) { drm_sg_cleanup(dev->sg); diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index a7b46ff80b0f..0f54ad8a9ced 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -283,6 +283,17 @@ static int drm_pci_agp_init(struct drm_device *dev) return 0; } +static void drm_pci_agp_destroy(struct drm_device *dev) +{ + if (drm_core_has_AGP(dev) && dev->agp) { + if (drm_core_has_MTRR(dev)) + arch_phys_wc_del(dev->agp->agp_mtrr); + drm_agp_clear(dev); + drm_agp_destroy(dev->agp); + dev->agp = NULL; + } +} + static struct drm_bus drm_pci_bus = { .bus_type = DRIVER_BUS_PCI, .get_irq = drm_pci_get_irq, @@ -291,6 +302,7 @@ static struct drm_bus drm_pci_bus = { .set_unique = drm_pci_set_unique, .irq_by_busid = drm_pci_irq_by_busid, .agp_init = drm_pci_agp_init, + .agp_destroy = drm_pci_agp_destroy, }; /** diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 327ca19cda85..d663f7d66dab 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -451,16 +451,11 @@ void drm_put_dev(struct drm_device *dev) drm_lastclose(dev); - if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) && dev->agp) - arch_phys_wc_del(dev->agp->agp_mtrr); - if (dev->driver->unload) dev->driver->unload(dev); - if (drm_core_has_AGP(dev) && dev->agp) { - kfree(dev->agp); - dev->agp = NULL; - } + if (dev->driver->bus->agp_destroy) + dev->driver->bus->agp_destroy(dev); drm_vblank_cleanup(dev); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 1a4eba627e79..fba547368a20 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -736,6 +736,7 @@ struct drm_bus { int (*irq_by_busid)(struct drm_device *dev, struct drm_irq_busid *p); /* hooks that are for PCI */ int (*agp_init)(struct drm_device *dev); + void (*agp_destroy)(struct drm_device *dev); }; @@ -1453,6 +1454,8 @@ extern int drm_modeset_ctl(struct drm_device *dev, void *data, /* AGP/GART support (drm_agpsupport.h) */ extern struct drm_agp_head *drm_agp_init(struct drm_device *dev); +extern void drm_agp_destroy(struct drm_agp_head *agp); +extern void drm_agp_clear(struct drm_device *dev); extern int drm_agp_acquire(struct drm_device *dev); extern int drm_agp_acquire_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -- cgit v1.2.3-71-gd317 From c700c67bae6698fbc6bd20e2ae5dc62ddd367b3b Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 27 Jul 2013 13:39:28 +0200 Subject: drm/mm: remove unused API We used to pre-allocate drm_mm nodes and save them in a linked list for later usage so we always have spare ones in atomic contexts. However, this is really racy if multiple threads are in an atomic context at the same time and we don't have enough spare nodes. Moreover, all remaining users run in user-context and just lock drm_mm with a spinlock. So we can easily preallocate the node, take the spinlock and insert the node. This may have worked well with BKL in place, however, with today's infrastructure it really doesn't make any sense. Besides, most users can easily embed drm_mm_node into their objects so no allocation is needed at all. Thus, remove the old pre-alloc API and all the helpers that it provides. Drivers have already been converted and we should not use the old API for new code, anymore. Signed-off-by: David Herrmann Acked-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_mm.c | 160 ++++++----------------------------------------- include/drm/drm_mm.h | 95 ---------------------------- 2 files changed, 20 insertions(+), 235 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 9a383272c8c7..aded1e11e8ff 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -49,58 +49,18 @@ #define MM_UNUSED_TARGET 4 -static struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic) -{ - struct drm_mm_node *child; - - if (atomic) - child = kzalloc(sizeof(*child), GFP_ATOMIC); - else - child = kzalloc(sizeof(*child), GFP_KERNEL); - - if (unlikely(child == NULL)) { - spin_lock(&mm->unused_lock); - if (list_empty(&mm->unused_nodes)) - child = NULL; - else { - child = - list_entry(mm->unused_nodes.next, - struct drm_mm_node, node_list); - list_del(&child->node_list); - --mm->num_unused; - } - spin_unlock(&mm->unused_lock); - } - return child; -} - -/* drm_mm_pre_get() - pre allocate drm_mm_node structure - * drm_mm: memory manager struct we are pre-allocating for - * - * Returns 0 on success or -ENOMEM if allocation fails. - */ -int drm_mm_pre_get(struct drm_mm *mm) -{ - struct drm_mm_node *node; - - spin_lock(&mm->unused_lock); - while (mm->num_unused < MM_UNUSED_TARGET) { - spin_unlock(&mm->unused_lock); - node = kzalloc(sizeof(*node), GFP_KERNEL); - spin_lock(&mm->unused_lock); - - if (unlikely(node == NULL)) { - int ret = (mm->num_unused < 2) ? -ENOMEM : 0; - spin_unlock(&mm->unused_lock); - return ret; - } - ++mm->num_unused; - list_add_tail(&node->node_list, &mm->unused_nodes); - } - spin_unlock(&mm->unused_lock); - return 0; -} -EXPORT_SYMBOL(drm_mm_pre_get); +static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, + unsigned long size, + unsigned alignment, + unsigned long color, + enum drm_mm_search_flags flags); +static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, + unsigned long size, + unsigned alignment, + unsigned long color, + unsigned long start, + unsigned long end, + enum drm_mm_search_flags flags); static void drm_mm_insert_helper(struct drm_mm_node *hole_node, struct drm_mm_node *node, @@ -187,24 +147,6 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) } EXPORT_SYMBOL(drm_mm_reserve_node); -struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node, - unsigned long size, - unsigned alignment, - unsigned long color, - int atomic) -{ - struct drm_mm_node *node; - - node = drm_mm_kmalloc(hole_node->mm, atomic); - if (unlikely(node == NULL)) - return NULL; - - drm_mm_insert_helper(hole_node, node, size, alignment, color); - - return node; -} -EXPORT_SYMBOL(drm_mm_get_block_generic); - /** * Search for free space and insert a preallocated memory node. Returns * -ENOSPC if no suitable free area is available. The preallocated memory node @@ -279,27 +221,6 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, } } -struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *hole_node, - unsigned long size, - unsigned alignment, - unsigned long color, - unsigned long start, - unsigned long end, - int atomic) -{ - struct drm_mm_node *node; - - node = drm_mm_kmalloc(hole_node->mm, atomic); - if (unlikely(node == NULL)) - return NULL; - - drm_mm_insert_helper_range(hole_node, node, size, alignment, color, - start, end); - - return node; -} -EXPORT_SYMBOL(drm_mm_get_block_range_generic); - /** * Search for free space and insert a preallocated memory node. Returns * -ENOSPC if no suitable free area is available. This is for range @@ -359,28 +280,6 @@ void drm_mm_remove_node(struct drm_mm_node *node) } EXPORT_SYMBOL(drm_mm_remove_node); -/* - * Remove a memory node from the allocator and free the allocated struct - * drm_mm_node. Only to be used on a struct drm_mm_node obtained by one of the - * drm_mm_get_block functions. - */ -void drm_mm_put_block(struct drm_mm_node *node) -{ - - struct drm_mm *mm = node->mm; - - drm_mm_remove_node(node); - - spin_lock(&mm->unused_lock); - if (mm->num_unused < MM_UNUSED_TARGET) { - list_add(&node->node_list, &mm->unused_nodes); - ++mm->num_unused; - } else - kfree(node); - spin_unlock(&mm->unused_lock); -} -EXPORT_SYMBOL(drm_mm_put_block); - static int check_free_hole(unsigned long start, unsigned long end, unsigned long size, unsigned alignment) { @@ -396,11 +295,11 @@ static int check_free_hole(unsigned long start, unsigned long end, return end >= start + size; } -struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, - unsigned long size, - unsigned alignment, - unsigned long color, - enum drm_mm_search_flags flags) +static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, + unsigned long size, + unsigned alignment, + unsigned long color, + enum drm_mm_search_flags flags) { struct drm_mm_node *entry; struct drm_mm_node *best; @@ -434,9 +333,8 @@ struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, return best; } -EXPORT_SYMBOL(drm_mm_search_free_generic); -struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, +static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, unsigned long size, unsigned alignment, unsigned long color, @@ -481,7 +379,6 @@ struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, return best; } -EXPORT_SYMBOL(drm_mm_search_free_in_range_generic); /** * Moves an allocation. To be used with embedded struct drm_mm_node. @@ -654,10 +551,7 @@ EXPORT_SYMBOL(drm_mm_clean); void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) { INIT_LIST_HEAD(&mm->hole_stack); - INIT_LIST_HEAD(&mm->unused_nodes); - mm->num_unused = 0; mm->scanned_blocks = 0; - spin_lock_init(&mm->unused_lock); /* Clever trick to avoid a special case in the free hole tracking. */ INIT_LIST_HEAD(&mm->head_node.node_list); @@ -677,22 +571,8 @@ EXPORT_SYMBOL(drm_mm_init); void drm_mm_takedown(struct drm_mm * mm) { - struct drm_mm_node *entry, *next; - - if (WARN(!list_empty(&mm->head_node.node_list), - "Memory manager not clean. Delaying takedown\n")) { - return; - } - - spin_lock(&mm->unused_lock); - list_for_each_entry_safe(entry, next, &mm->unused_nodes, node_list) { - list_del(&entry->node_list); - kfree(entry); - --mm->num_unused; - } - spin_unlock(&mm->unused_lock); - - BUG_ON(mm->num_unused != 0); + WARN(!list_empty(&mm->head_node.node_list), + "Memory manager not clean during takedown.\n"); } EXPORT_SYMBOL(drm_mm_takedown); diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h index 439d1a17d3b1..cba67865d18f 100644 --- a/include/drm/drm_mm.h +++ b/include/drm/drm_mm.h @@ -70,9 +70,6 @@ struct drm_mm { /* head_node.node_list is the list of all memory nodes, ordered * according to the (increasing) start address of the memory node. */ struct drm_mm_node head_node; - struct list_head unused_nodes; - int num_unused; - spinlock_t unused_lock; unsigned int scan_check_range : 1; unsigned scan_alignment; unsigned long scan_color; @@ -123,13 +120,6 @@ static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node) #define drm_mm_for_each_node(entry, mm) list_for_each_entry(entry, \ &(mm)->head_node.node_list, \ node_list) -#define drm_mm_for_each_scanned_node_reverse(entry, n, mm) \ - for (entry = (mm)->prev_scanned_node, \ - next = entry ? list_entry(entry->node_list.next, \ - struct drm_mm_node, node_list) : NULL; \ - entry != NULL; entry = next, \ - next = entry ? list_entry(entry->node_list.next, \ - struct drm_mm_node, node_list) : NULL) \ /* Note that we need to unroll list_for_each_entry in order to inline * setting hole_start and hole_end on each iteration and keep the @@ -147,52 +137,6 @@ static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node) * Basic range manager support (drm_mm.c) */ extern int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node); -extern struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node, - unsigned long size, - unsigned alignment, - unsigned long color, - int atomic); -extern struct drm_mm_node *drm_mm_get_block_range_generic( - struct drm_mm_node *node, - unsigned long size, - unsigned alignment, - unsigned long color, - unsigned long start, - unsigned long end, - int atomic); - -static inline struct drm_mm_node *drm_mm_get_block(struct drm_mm_node *parent, - unsigned long size, - unsigned alignment) -{ - return drm_mm_get_block_generic(parent, size, alignment, 0, 0); -} -static inline struct drm_mm_node *drm_mm_get_block_atomic(struct drm_mm_node *parent, - unsigned long size, - unsigned alignment) -{ - return drm_mm_get_block_generic(parent, size, alignment, 0, 1); -} -static inline struct drm_mm_node *drm_mm_get_block_range( - struct drm_mm_node *parent, - unsigned long size, - unsigned alignment, - unsigned long start, - unsigned long end) -{ - return drm_mm_get_block_range_generic(parent, size, alignment, 0, - start, end, 0); -} -static inline struct drm_mm_node *drm_mm_get_block_atomic_range( - struct drm_mm_node *parent, - unsigned long size, - unsigned alignment, - unsigned long start, - unsigned long end) -{ - return drm_mm_get_block_range_generic(parent, size, alignment, 0, - start, end, 1); -} extern int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, @@ -229,52 +173,13 @@ static inline int drm_mm_insert_node_in_range(struct drm_mm *mm, 0, start, end, flags); } -extern void drm_mm_put_block(struct drm_mm_node *cur); extern void drm_mm_remove_node(struct drm_mm_node *node); extern void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new); -extern struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, - unsigned long size, - unsigned alignment, - unsigned long color, - enum drm_mm_search_flags flags); -extern struct drm_mm_node *drm_mm_search_free_in_range_generic( - const struct drm_mm *mm, - unsigned long size, - unsigned alignment, - unsigned long color, - unsigned long start, - unsigned long end, - enum drm_mm_search_flags flags); -static inline struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm, - unsigned long size, - unsigned alignment, - enum drm_mm_search_flags flags) -{ - return drm_mm_search_free_generic(mm,size, alignment, 0, flags); -} -static inline struct drm_mm_node *drm_mm_search_free_in_range( - const struct drm_mm *mm, - unsigned long size, - unsigned alignment, - unsigned long start, - unsigned long end, - enum drm_mm_search_flags flags) -{ - return drm_mm_search_free_in_range_generic(mm, size, alignment, 0, - start, end, flags); -} - extern void drm_mm_init(struct drm_mm *mm, unsigned long start, unsigned long size); extern void drm_mm_takedown(struct drm_mm *mm); extern int drm_mm_clean(struct drm_mm *mm); -extern int drm_mm_pre_get(struct drm_mm *mm); - -static inline struct drm_mm *drm_get_mm(struct drm_mm_node *block) -{ - return block->mm; -} void drm_mm_init_scan(struct drm_mm *mm, unsigned long size, -- cgit v1.2.3-71-gd317 From 36f8250e4d478429a393c20910dcd02772bd0839 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 7 Aug 2013 08:56:10 +0800 Subject: regulator: fan53555: Prevent multiple include of fan53555.h Add define for __FAN53555_H__ to prevent multiple include of the header file. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- include/linux/regulator/fan53555.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/regulator/fan53555.h b/include/linux/regulator/fan53555.h index 5c45c85d52ca..f13880e84d85 100644 --- a/include/linux/regulator/fan53555.h +++ b/include/linux/regulator/fan53555.h @@ -11,6 +11,7 @@ */ #ifndef __FAN53555_H__ +#define __FAN53555_H__ /* VSEL ID */ enum { -- cgit v1.2.3-71-gd317 From 9d3dda5c0d092d6bc9911bf24de81350d47c6be6 Mon Sep 17 00:00:00 2001 From: Leilei Shang Date: Fri, 7 Jun 2013 14:38:17 +0800 Subject: i2c: pxa: enable high speed mode for i2c bus To enter high speed mode, following steps should be done: 1. When running in high speed mode, i2c clock rate is different from standard mode. Clock rate must be set according to specification first. 2. When i2c controller sends a master code and wins arbitration, high speed mode is entered. If you want to enable high speed mode, the following members of platform data should be set to proper value: 1. "high_mode" should be set to "1". 2. "master_code" should be set to "8'b 0000_1xxx"(x is 0 or 1). If no master_code is set, set to default value 0xe. 3. "rate" should be set according to specification. Signed-off-by: Leilei Shang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-pxa.c | 64 +++++++++++++++++++++++++++++++++++++++++++- include/linux/i2c/pxa-i2c.h | 3 +++ 2 files changed, 66 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index fbafed29fb81..a61d9062bf7b 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -110,6 +110,8 @@ MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table); #define ICR_SADIE (1 << 13) /* slave address detected int enable */ #define ICR_UR (1 << 14) /* unit reset */ #define ICR_FM (1 << 15) /* fast mode */ +#define ICR_HS (1 << 16) /* High Speed mode */ +#define ICR_GPIOEN (1 << 19) /* enable GPIO mode for SCL in HS */ #define ISR_RWM (1 << 0) /* read/write mode */ #define ISR_ACKNAK (1 << 1) /* ack/nak status */ @@ -155,6 +157,10 @@ struct pxa_i2c { int irq; unsigned int use_pio :1; unsigned int fast_mode :1; + unsigned int high_mode:1; + unsigned char master_code; + unsigned long rate; + bool highmode_enter; }; #define _IBMR(i2c) ((i2c)->reg_ibmr) @@ -459,6 +465,7 @@ static void i2c_pxa_reset(struct pxa_i2c *i2c) /* set control register values */ writel(I2C_ICR_INIT | (i2c->fast_mode ? ICR_FM : 0), _ICR(i2c)); + writel(readl(_ICR(i2c)) | (i2c->high_mode ? ICR_HS : 0), _ICR(i2c)); #ifdef CONFIG_I2C_PXA_SLAVE dev_info(&i2c->adap.dev, "Enabling slave mode\n"); @@ -680,6 +687,34 @@ static int i2c_pxa_pio_set_master(struct pxa_i2c *i2c) return 0; } +/* + * PXA I2C send master code + * 1. Load master code to IDBR and send it. + * Note for HS mode, set ICR [GPIOEN]. + * 2. Wait until win arbitration. + */ +static int i2c_pxa_send_mastercode(struct pxa_i2c *i2c) +{ + u32 icr; + long timeout; + + spin_lock_irq(&i2c->lock); + i2c->highmode_enter = true; + writel(i2c->master_code, _IDBR(i2c)); + + icr = readl(_ICR(i2c)) & ~(ICR_STOP | ICR_ALDIE); + icr |= ICR_GPIOEN | ICR_START | ICR_TB | ICR_ITEIE; + writel(icr, _ICR(i2c)); + + spin_unlock_irq(&i2c->lock); + timeout = wait_event_timeout(i2c->wait, + i2c->highmode_enter == false, HZ * 1); + + i2c->highmode_enter = false; + + return (timeout == 0) ? I2C_RETRY : 0; +} + static int i2c_pxa_do_pio_xfer(struct pxa_i2c *i2c, struct i2c_msg *msg, int num) { @@ -743,6 +778,14 @@ static int i2c_pxa_do_xfer(struct pxa_i2c *i2c, struct i2c_msg *msg, int num) goto out; } + if (i2c->high_mode) { + ret = i2c_pxa_send_mastercode(i2c); + if (ret) { + dev_err(&i2c->adap.dev, "i2c_pxa_send_mastercode timeout\n"); + goto out; + } + } + spin_lock_irq(&i2c->lock); i2c->msg = msg; @@ -990,11 +1033,14 @@ static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id) i2c_pxa_slave_txempty(i2c, isr); if (isr & ISR_IRF) i2c_pxa_slave_rxfull(i2c, isr); - } else if (i2c->msg) { + } else if (i2c->msg && (!i2c->highmode_enter)) { if (isr & ISR_ITE) i2c_pxa_irq_txempty(i2c, isr); if (isr & ISR_IRF) i2c_pxa_irq_rxfull(i2c, isr); + } else if ((isr & ISR_ITE) && i2c->highmode_enter) { + i2c->highmode_enter = false; + wake_up(&i2c->wait); } else { i2c_pxa_scream_blue_murder(i2c, "spurious irq"); } @@ -1079,6 +1125,11 @@ static int i2c_pxa_probe_pdata(struct platform_device *pdev, if (plat) { i2c->use_pio = plat->use_pio; i2c->fast_mode = plat->fast_mode; + i2c->high_mode = plat->high_mode; + i2c->master_code = plat->master_code; + if (!i2c->master_code) + i2c->master_code = 0xe; + i2c->rate = plat->rate; } return 0; } @@ -1151,6 +1202,7 @@ static int i2c_pxa_probe(struct platform_device *dev) i2c->irq = irq; i2c->slave_addr = I2C_PXA_SLAVE_ADDR; + i2c->highmode_enter = false; if (plat) { #ifdef CONFIG_I2C_PXA_SLAVE @@ -1160,6 +1212,16 @@ static int i2c_pxa_probe(struct platform_device *dev) i2c->adap.class = plat->class; } + if (i2c->high_mode) { + if (i2c->rate) { + clk_set_rate(i2c->clk, i2c->rate); + pr_info("i2c: <%s> set rate to %ld\n", + i2c->adap.name, clk_get_rate(i2c->clk)); + } else + pr_warn("i2c: <%s> clock rate not set\n", + i2c->adap.name); + } + clk_prepare_enable(i2c->clk); if (i2c->use_pio) { diff --git a/include/linux/i2c/pxa-i2c.h b/include/linux/i2c/pxa-i2c.h index 1a9f65e6ec0f..53aab243cbd8 100644 --- a/include/linux/i2c/pxa-i2c.h +++ b/include/linux/i2c/pxa-i2c.h @@ -67,6 +67,9 @@ struct i2c_pxa_platform_data { unsigned int class; unsigned int use_pio :1; unsigned int fast_mode :1; + unsigned int high_mode:1; + unsigned char master_code; + unsigned long rate; }; extern void pxa_set_i2c_info(struct i2c_pxa_platform_data *info); -- cgit v1.2.3-71-gd317 From 3923172b3d700486c1ca24df9c4c5405a83e2309 Mon Sep 17 00:00:00 2001 From: Phil Carmody Date: Wed, 26 Jun 2013 10:56:35 +0300 Subject: i2c: reduce parent checking to a NOOP in non-I2C_MUX case In 0826374 - i2c: Multiplexed I2C bus core support core i2c code increased in size and complexity even when I2C_MUX wasn't selected. Turning this check into a constant NULL in the n case lets the client functions in be simplified too, not needing to include never-called calls to the mux-specific helpers. Signed-off-by: Phil Carmody Signed-off-by: Wolfram Sang --- include/linux/i2c.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/i2c.h b/include/linux/i2c.h index e988fa935b3c..b3c4b8dac1ce 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -447,11 +447,13 @@ static inline void i2c_set_adapdata(struct i2c_adapter *dev, void *data) static inline struct i2c_adapter * i2c_parent_is_i2c_adapter(const struct i2c_adapter *adapter) { +#if IS_ENABLED(I2C_MUX) struct device *parent = adapter->dev.parent; if (parent != NULL && parent->type == &i2c_adapter_type) return to_i2c_adapter(parent); else +#endif return NULL; } -- cgit v1.2.3-71-gd317 From f8407299f61681e9733f06c7214e81002cb459b3 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 24 Jul 2013 11:59:49 -0400 Subject: NFS Remove unused authflavour parameter from init_client Signed-off-by: Andy Adamson Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 6 ++---- fs/nfs/internal.h | 5 ++--- fs/nfs/nfs4client.c | 3 +-- include/linux/nfs_xdr.h | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 340b1eff0267..2dceee4db076 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -501,8 +501,7 @@ nfs_get_client(const struct nfs_client_initdata *cl_init, &nn->nfs_client_list); spin_unlock(&nn->nfs_client_lock); new->cl_flags = cl_init->init_flags; - return rpc_ops->init_client(new, timeparms, ip_addr, - authflavour); + return rpc_ops->init_client(new, timeparms, ip_addr); } spin_unlock(&nn->nfs_client_lock); @@ -694,13 +693,12 @@ EXPORT_SYMBOL_GPL(nfs_init_server_rpcclient); * @clp: nfs_client to initialise * @timeparms: timeout parameters for underlying RPC transport * @ip_addr: IP presentation address (not used) - * @authflavor: authentication flavor for underlying RPC transport * * Returns pointer to an NFS client, or an ERR_PTR value. */ struct nfs_client *nfs_init_client(struct nfs_client *clp, const struct rpc_timeout *timeparms, - const char *ip_addr, rpc_authflavor_t authflavour) + const char *ip_addr) { int error; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 3c8373f90ab3..9b694f1e06c5 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -267,7 +267,7 @@ extern struct rpc_procinfo nfs4_procedures[]; void nfs_close_context(struct nfs_open_context *ctx, int is_sync); extern struct nfs_client *nfs_init_client(struct nfs_client *clp, const struct rpc_timeout *timeparms, - const char *ip_addr, rpc_authflavor_t authflavour); + const char *ip_addr); /* dir.c */ extern int nfs_access_cache_shrinker(struct shrinker *shrink, @@ -451,8 +451,7 @@ extern ssize_t nfs_dreq_bytes_left(struct nfs_direct_req *dreq); extern void __nfs4_read_done_cb(struct nfs_read_data *); extern struct nfs_client *nfs4_init_client(struct nfs_client *clp, const struct rpc_timeout *timeparms, - const char *ip_addr, - rpc_authflavor_t authflavour); + const char *ip_addr); extern int nfs40_walk_client_list(struct nfs_client *clp, struct nfs_client **result, struct rpc_cred *cred); diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 90dce91dd5b5..767a5e37fe97 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -187,8 +187,7 @@ static int nfs4_init_client_minor_version(struct nfs_client *clp) */ struct nfs_client *nfs4_init_client(struct nfs_client *clp, const struct rpc_timeout *timeparms, - const char *ip_addr, - rpc_authflavor_t authflavour) + const char *ip_addr) { char buf[INET6_ADDRSTRLEN + 1]; struct nfs_client *old; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 8651574a305b..ddc3e32178c3 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1442,7 +1442,7 @@ struct nfs_rpc_ops { struct nfs_client *(*alloc_client) (const struct nfs_client_initdata *); struct nfs_client * (*init_client) (struct nfs_client *, const struct rpc_timeout *, - const char *, rpc_authflavor_t); + const char *); void (*free_client) (struct nfs_client *); struct nfs_server *(*create_server)(struct nfs_mount_info *, struct nfs_subversion *); struct nfs_server *(*clone_server)(struct nfs_server *, struct nfs_fh *, -- cgit v1.2.3-71-gd317 From bf4dae5ce1b95a5932e43036edcf3f1b324758c6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 7 Aug 2013 22:21:57 +0200 Subject: pinctrl: nomadik: delete ancient pin control API The pin control subsystem was created to do away with custom pin control APIs such as this one. It was kept for backward-compatibility but is completely unused in the current kernel, so let's delete it. Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-nomadik.c | 295 -------------------------- include/linux/platform_data/pinctrl-nomadik.h | 24 --- 2 files changed, 319 deletions(-) (limited to 'include') diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index 4a1cfdce2232..89280bc8123c 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -337,97 +337,6 @@ static void nmk_prcm_altcx_set_mode(struct nmk_pinctrl *npct, nmk_write_masked(npct->prcm_base + reg, BIT(bit), BIT(bit)); } -static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, - pin_cfg_t cfg, bool sleep, unsigned int *slpmregs) -{ - static const char *afnames[] = { - [NMK_GPIO_ALT_GPIO] = "GPIO", - [NMK_GPIO_ALT_A] = "A", - [NMK_GPIO_ALT_B] = "B", - [NMK_GPIO_ALT_C] = "C" - }; - static const char *pullnames[] = { - [NMK_GPIO_PULL_NONE] = "none", - [NMK_GPIO_PULL_UP] = "up", - [NMK_GPIO_PULL_DOWN] = "down", - [3] /* illegal */ = "??" - }; - static const char *slpmnames[] = { - [NMK_GPIO_SLPM_INPUT] = "input/wakeup", - [NMK_GPIO_SLPM_NOCHANGE] = "no-change/no-wakeup", - }; - - int pin = PIN_NUM(cfg); - int pull = PIN_PULL(cfg); - int af = PIN_ALT(cfg); - int slpm = PIN_SLPM(cfg); - int output = PIN_DIR(cfg); - int val = PIN_VAL(cfg); - bool glitch = af == NMK_GPIO_ALT_C; - - dev_dbg(nmk_chip->chip.dev, "pin %d [%#lx]: af %s, pull %s, slpm %s (%s%s)\n", - pin, cfg, afnames[af], pullnames[pull], slpmnames[slpm], - output ? "output " : "input", - output ? (val ? "high" : "low") : ""); - - if (sleep) { - int slpm_pull = PIN_SLPM_PULL(cfg); - int slpm_output = PIN_SLPM_DIR(cfg); - int slpm_val = PIN_SLPM_VAL(cfg); - - af = NMK_GPIO_ALT_GPIO; - - /* - * The SLPM_* values are normal values + 1 to allow zero to - * mean "same as normal". - */ - if (slpm_pull) - pull = slpm_pull - 1; - if (slpm_output) - output = slpm_output - 1; - if (slpm_val) - val = slpm_val - 1; - - dev_dbg(nmk_chip->chip.dev, "pin %d: sleep pull %s, dir %s, val %s\n", - pin, - slpm_pull ? pullnames[pull] : "same", - slpm_output ? (output ? "output" : "input") : "same", - slpm_val ? (val ? "high" : "low") : "same"); - } - - if (output) - __nmk_gpio_make_output(nmk_chip, offset, val); - else { - __nmk_gpio_make_input(nmk_chip, offset); - __nmk_gpio_set_pull(nmk_chip, offset, pull); - } - - __nmk_gpio_set_lowemi(nmk_chip, offset, PIN_LOWEMI(cfg)); - - /* - * If the pin is switching to altfunc, and there was an interrupt - * installed on it which has been lazy disabled, actually mask the - * interrupt to prevent spurious interrupts that would occur while the - * pin is under control of the peripheral. Only SKE does this. - */ - if (af != NMK_GPIO_ALT_GPIO) - nmk_gpio_disable_lazy_irq(nmk_chip, offset); - - /* - * If we've backed up the SLPM registers (glitch workaround), modify - * the backups since they will be restored. - */ - if (slpmregs) { - if (slpm == NMK_GPIO_SLPM_NOCHANGE) - slpmregs[nmk_chip->bank] |= BIT(offset); - else - slpmregs[nmk_chip->bank] &= ~BIT(offset); - } else - __nmk_gpio_set_slpm(nmk_chip, offset, slpm); - - __nmk_gpio_set_mode_safe(nmk_chip, offset, af, glitch); -} - /* * Safe sequence used to switch IOs between GPIO and Alternate-C mode: * - Save SLPM registers @@ -474,210 +383,6 @@ static void nmk_gpio_glitch_slpm_restore(unsigned int *slpm) } } -static int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep) -{ - static unsigned int slpm[NUM_BANKS]; - unsigned long flags; - bool glitch = false; - int ret = 0; - int i; - - for (i = 0; i < num; i++) { - if (PIN_ALT(cfgs[i]) == NMK_GPIO_ALT_C) { - glitch = true; - break; - } - } - - spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); - - if (glitch) { - memset(slpm, 0xff, sizeof(slpm)); - - for (i = 0; i < num; i++) { - int pin = PIN_NUM(cfgs[i]); - int offset = pin % NMK_GPIO_PER_CHIP; - - if (PIN_ALT(cfgs[i]) == NMK_GPIO_ALT_C) - slpm[pin / NMK_GPIO_PER_CHIP] &= ~BIT(offset); - } - - nmk_gpio_glitch_slpm_init(slpm); - } - - for (i = 0; i < num; i++) { - struct nmk_gpio_chip *nmk_chip; - int pin = PIN_NUM(cfgs[i]); - - nmk_chip = nmk_gpio_chips[pin / NMK_GPIO_PER_CHIP]; - if (!nmk_chip) { - ret = -EINVAL; - break; - } - - clk_enable(nmk_chip->clk); - spin_lock(&nmk_chip->lock); - __nmk_config_pin(nmk_chip, pin % NMK_GPIO_PER_CHIP, - cfgs[i], sleep, glitch ? slpm : NULL); - spin_unlock(&nmk_chip->lock); - clk_disable(nmk_chip->clk); - } - - if (glitch) - nmk_gpio_glitch_slpm_restore(slpm); - - spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); - - return ret; -} - -/** - * nmk_config_pin - configure a pin's mux attributes - * @cfg: pin confguration - * @sleep: Non-zero to apply the sleep mode configuration - * Configures a pin's mode (alternate function or GPIO), its pull up status, - * and its sleep mode based on the specified configuration. The @cfg is - * usually one of the SoC specific macros defined in mach/-pins.h. These - * are constructed using, and can be further enhanced with, the macros in - * - * - * If a pin's mode is set to GPIO, it is configured as an input to avoid - * side-effects. The gpio can be manipulated later using standard GPIO API - * calls. - */ -int nmk_config_pin(pin_cfg_t cfg, bool sleep) -{ - return __nmk_config_pins(&cfg, 1, sleep); -} -EXPORT_SYMBOL(nmk_config_pin); - -/** - * nmk_config_pins - configure several pins at once - * @cfgs: array of pin configurations - * @num: number of elments in the array - * - * Configures several pins using nmk_config_pin(). Refer to that function for - * further information. - */ -int nmk_config_pins(pin_cfg_t *cfgs, int num) -{ - return __nmk_config_pins(cfgs, num, false); -} -EXPORT_SYMBOL(nmk_config_pins); - -int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num) -{ - return __nmk_config_pins(cfgs, num, true); -} -EXPORT_SYMBOL(nmk_config_pins_sleep); - -/** - * nmk_gpio_set_slpm() - configure the sleep mode of a pin - * @gpio: pin number - * @mode: NMK_GPIO_SLPM_INPUT or NMK_GPIO_SLPM_NOCHANGE, - * - * This register is actually in the pinmux layer, not the GPIO block itself. - * The GPIO1B_SLPM register defines the GPIO mode when SLEEP/DEEP-SLEEP - * mode is entered (i.e. when signal IOFORCE is HIGH by the platform code). - * Each GPIO can be configured to be forced into GPIO mode when IOFORCE is - * HIGH, overriding the normal setting defined by GPIO_AFSELx registers. - * When IOFORCE returns LOW (by software, after SLEEP/DEEP-SLEEP exit), - * the GPIOs return to the normal setting defined by GPIO_AFSELx registers. - * - * If @mode is NMK_GPIO_SLPM_INPUT, the corresponding GPIO is switched to GPIO - * mode when signal IOFORCE is HIGH (i.e. when SLEEP/DEEP-SLEEP mode is - * entered) regardless of the altfunction selected. Also wake-up detection is - * ENABLED. - * - * If @mode is NMK_GPIO_SLPM_NOCHANGE, the corresponding GPIO remains - * controlled by NMK_GPIO_DATC, NMK_GPIO_DATS, NMK_GPIO_DIR, NMK_GPIO_PDIS - * (for altfunction GPIO) or respective on-chip peripherals (for other - * altfuncs) when IOFORCE is HIGH. Also wake-up detection DISABLED. - * - * Note that enable_irq_wake() will automatically enable wakeup detection. - */ -int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode) -{ - struct nmk_gpio_chip *nmk_chip; - unsigned long flags; - - nmk_chip = nmk_gpio_chips[gpio / NMK_GPIO_PER_CHIP]; - if (!nmk_chip) - return -EINVAL; - - clk_enable(nmk_chip->clk); - spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); - spin_lock(&nmk_chip->lock); - - __nmk_gpio_set_slpm(nmk_chip, gpio % NMK_GPIO_PER_CHIP, mode); - - spin_unlock(&nmk_chip->lock); - spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); - clk_disable(nmk_chip->clk); - - return 0; -} - -/** - * nmk_gpio_set_pull() - enable/disable pull up/down on a gpio - * @gpio: pin number - * @pull: one of NMK_GPIO_PULL_DOWN, NMK_GPIO_PULL_UP, and NMK_GPIO_PULL_NONE - * - * Enables/disables pull up/down on a specified pin. This only takes effect if - * the pin is configured as an input (either explicitly or by the alternate - * function). - * - * NOTE: If enabling the pull up/down, the caller must ensure that the GPIO is - * configured as an input. Otherwise, due to the way the controller registers - * work, this function will change the value output on the pin. - */ -int nmk_gpio_set_pull(int gpio, enum nmk_gpio_pull pull) -{ - struct nmk_gpio_chip *nmk_chip; - unsigned long flags; - - nmk_chip = nmk_gpio_chips[gpio / NMK_GPIO_PER_CHIP]; - if (!nmk_chip) - return -EINVAL; - - clk_enable(nmk_chip->clk); - spin_lock_irqsave(&nmk_chip->lock, flags); - __nmk_gpio_set_pull(nmk_chip, gpio % NMK_GPIO_PER_CHIP, pull); - spin_unlock_irqrestore(&nmk_chip->lock, flags); - clk_disable(nmk_chip->clk); - - return 0; -} - -/* Mode functions */ -/** - * nmk_gpio_set_mode() - set the mux mode of a gpio pin - * @gpio: pin number - * @gpio_mode: one of NMK_GPIO_ALT_GPIO, NMK_GPIO_ALT_A, - * NMK_GPIO_ALT_B, and NMK_GPIO_ALT_C - * - * Sets the mode of the specified pin to one of the alternate functions or - * plain GPIO. - */ -int nmk_gpio_set_mode(int gpio, int gpio_mode) -{ - struct nmk_gpio_chip *nmk_chip; - unsigned long flags; - - nmk_chip = nmk_gpio_chips[gpio / NMK_GPIO_PER_CHIP]; - if (!nmk_chip) - return -EINVAL; - - clk_enable(nmk_chip->clk); - spin_lock_irqsave(&nmk_chip->lock, flags); - __nmk_gpio_set_mode(nmk_chip, gpio % NMK_GPIO_PER_CHIP, gpio_mode); - spin_unlock_irqrestore(&nmk_chip->lock, flags); - clk_disable(nmk_chip->clk); - - return 0; -} -EXPORT_SYMBOL(nmk_gpio_set_mode); - static int __maybe_unused nmk_prcm_gpiocr_get_mode(struct pinctrl_dev *pctldev, int gpio) { int i; diff --git a/include/linux/platform_data/pinctrl-nomadik.h b/include/linux/platform_data/pinctrl-nomadik.h index f73b2f0c55b7..abf5bed84df3 100644 --- a/include/linux/platform_data/pinctrl-nomadik.h +++ b/include/linux/platform_data/pinctrl-nomadik.h @@ -226,30 +226,6 @@ enum nmk_gpio_slpm { NMK_GPIO_SLPM_WAKEUP_DISABLE = NMK_GPIO_SLPM_NOCHANGE, }; -/* Older deprecated pin config API that should go away soon */ -extern int nmk_config_pin(pin_cfg_t cfg, bool sleep); -extern int nmk_config_pins(pin_cfg_t *cfgs, int num); -extern int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num); -extern int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode); -extern int nmk_gpio_set_pull(int gpio, enum nmk_gpio_pull pull); -#ifdef CONFIG_PINCTRL_NOMADIK -extern int nmk_gpio_set_mode(int gpio, int gpio_mode); -#else -static inline int nmk_gpio_set_mode(int gpio, int gpio_mode) -{ - return -ENODEV; -} -#endif -extern int nmk_gpio_get_mode(int gpio); - -extern void nmk_gpio_wakeups_suspend(void); -extern void nmk_gpio_wakeups_resume(void); - -extern void nmk_gpio_clocks_enable(void); -extern void nmk_gpio_clocks_disable(void); - -extern void nmk_gpio_read_pull(int gpio_bank, u32 *pull_up); - /* * Platform data to register a block: only the initial gpio/irq number. */ -- cgit v1.2.3-71-gd317 From cf4957f17f2a89984915ea808876d9c82225b862 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 24 Oct 2012 13:37:58 +0200 Subject: perf: Add PERF_EVENT_IOC_ID ioctl to return event ID The only way to get the event ID is by reading the event fd, followed by parsing the ID value out of the returned data. While this is ok for current read format used by perf tool, it is not ok when we use PERF_FORMAT_GROUP format. With this format the data are returned for the whole group and there's no way to find out what ID belongs to our fd (if we are not group leader event). Adding a simple ioctl that returns event primary ID for given fd. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Acked-by: Peter Zijlstra Cc: Corey Ashford Cc: Frederic Weisbecker Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-v1bn5cto707jn0bon34afqr1@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- include/uapi/linux/perf_event.h | 1 + kernel/events/core.c | 9 +++++++++ 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index efef1d37a371..62c25a25291c 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -321,6 +321,7 @@ struct perf_event_attr { #define PERF_EVENT_IOC_PERIOD _IOW('$', 4, __u64) #define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) #define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *) +#define PERF_EVENT_IOC_ID _IOR('$', 7, u64 *) enum perf_event_ioc_flags { PERF_IOC_FLAG_GROUP = 1U << 0, diff --git a/kernel/events/core.c b/kernel/events/core.c index 916cf1f593b4..5200b608b481 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3568,6 +3568,15 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PERF_EVENT_IOC_PERIOD: return perf_event_period(event, (u64 __user *)arg); + case PERF_EVENT_IOC_ID: + { + u64 id = primary_event_id(event); + + if (copy_to_user((void __user *)arg, &id, sizeof(id))) + return -EFAULT; + return 0; + } + case PERF_EVENT_IOC_SET_OUTPUT: { int ret; -- cgit v1.2.3-71-gd317 From 5ff0a268037d344f86df690ccb994d8bc015d2d9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 6 Aug 2013 22:53:03 +0530 Subject: cpufreq: Clean up header files included in the core This patch addresses the following issues in the header files in the cpufreq core: - Include headers in ascending order, so that we don't add same many times by mistake. - must be included after , so that they override whatever they need to. - Remove unnecessary includes. - Don't include files already included by cpufreq.h or cpufreq_governor.h. [rjw: Changelog] Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 19 ++++++------------- drivers/cpufreq/cpufreq_conservative.c | 12 ------------ drivers/cpufreq/cpufreq_governor.c | 6 ------ drivers/cpufreq/cpufreq_governor.h | 5 ++--- drivers/cpufreq/cpufreq_ondemand.c | 12 +----------- drivers/cpufreq/cpufreq_performance.c | 3 +-- drivers/cpufreq/cpufreq_powersave.c | 3 +-- drivers/cpufreq/cpufreq_stats.c | 9 +-------- drivers/cpufreq/freq_table.c | 4 +--- include/linux/cpufreq.h | 11 +++-------- 10 files changed, 16 insertions(+), 68 deletions(-) (limited to 'include') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index e34bd94e12b4..845837174878 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -17,24 +17,17 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include +#include #include #include -#include -#include -#include #include -#include -#include -#include +#include +#include +#include #include +#include #include - +#include #include /** diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 0ceb2eff5a7e..841e25699656 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -11,19 +11,7 @@ * published by the Free Software Foundation. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include - #include "cpufreq_governor.h" /* Conservative governor macros */ diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 7409dbd1d897..556064e9cc01 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -16,15 +16,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include #include #include -#include #include -#include -#include #include "cpufreq_governor.h" diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h index 0e0dd4c82020..cf0b7a4425b0 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -18,10 +18,9 @@ #define _CPUFREQ_GOVERNOR_H #include -#include +#include +#include #include -#include -#include /* * The polling frequency depends on the capability of the processor. Default diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index a3c5574f9b3a..27c732eee431 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -12,20 +12,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include -#include -#include -#include -#include +#include #include #include -#include #include -#include -#include - #include "cpufreq_governor.h" /* On-demand governor macros */ diff --git a/drivers/cpufreq/cpufreq_performance.c b/drivers/cpufreq/cpufreq_performance.c index 9fef7d6e4e6a..cf117deb39b1 100644 --- a/drivers/cpufreq/cpufreq_performance.c +++ b/drivers/cpufreq/cpufreq_performance.c @@ -12,10 +12,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include #include #include +#include static int cpufreq_governor_performance(struct cpufreq_policy *policy, unsigned int event) diff --git a/drivers/cpufreq/cpufreq_powersave.c b/drivers/cpufreq/cpufreq_powersave.c index 32109a14f5dc..e3b874c235ea 100644 --- a/drivers/cpufreq/cpufreq_powersave.c +++ b/drivers/cpufreq/cpufreq_powersave.c @@ -12,10 +12,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include #include #include +#include static int cpufreq_governor_powersave(struct cpufreq_policy *policy, unsigned int event) diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index cb3841355454..a7143b0597af 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -9,17 +9,10 @@ * published by the Free Software Foundation. */ -#include -#include #include -#include #include #include -#include -#include -#include -#include -#include +#include #include static spinlock_t cpufreq_stats_lock; diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index f0d87412cc91..f111454a7aea 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -11,10 +11,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include -#include -#include #include +#include /********************************************************************* * FREQUENCY TABLE HELPERS * diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index e1fd215e16c9..97627bbbca82 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -11,16 +11,11 @@ #ifndef _LINUX_CPUFREQ_H #define _LINUX_CPUFREQ_H -#include -#include -#include -#include +#include +#include #include +#include #include -#include -#include -#include -#include #define CPUFREQ_NAME_LEN 16 /* Print length for names. Extra 1 space for accomodating '\n' in prints */ -- cgit v1.2.3-71-gd317 From 74aca95da74836a6807118f6590d8df8232c74a9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 6 Aug 2013 22:53:04 +0530 Subject: cpufreq: Re-arrange declarations in cpufreq.h They are pretty much mixed up. Although generic headers are present, definitions/declarations are present outside of them too ... This patch just moves stuff up and down to make it look better and consistent. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- include/linux/cpufreq.h | 373 +++++++++++++++++++++++------------------------- 1 file changed, 177 insertions(+), 196 deletions(-) (limited to 'include') diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 97627bbbca82..0d3b0266bc98 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -17,60 +17,30 @@ #include #include -#define CPUFREQ_NAME_LEN 16 -/* Print length for names. Extra 1 space for accomodating '\n' in prints */ -#define CPUFREQ_NAME_PLEN (CPUFREQ_NAME_LEN + 1) - /********************************************************************* - * CPUFREQ NOTIFIER INTERFACE * + * CPUFREQ INTERFACE * *********************************************************************/ - -#define CPUFREQ_TRANSITION_NOTIFIER (0) -#define CPUFREQ_POLICY_NOTIFIER (1) - -#ifdef CONFIG_CPU_FREQ -int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list); -int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list); -extern void disable_cpufreq(void); -#else /* CONFIG_CPU_FREQ */ -static inline int cpufreq_register_notifier(struct notifier_block *nb, - unsigned int list) -{ - return 0; -} -static inline int cpufreq_unregister_notifier(struct notifier_block *nb, - unsigned int list) -{ - return 0; -} -static inline void disable_cpufreq(void) { } -#endif /* CONFIG_CPU_FREQ */ - -/* if (cpufreq_driver->target) exists, the ->governor decides what frequency - * within the limits is used. If (cpufreq_driver->setpolicy> exists, these - * two generic policies are available: - */ - -#define CPUFREQ_POLICY_POWERSAVE (1) -#define CPUFREQ_POLICY_PERFORMANCE (2) - -/* Frequency values here are CPU kHz so that hardware which doesn't run - * with some frequencies can complain without having to guess what per - * cent / per mille means. +/* + * Frequency values here are CPU kHz + * * Maximum transition latency is in nanoseconds - if it's unknown, * CPUFREQ_ETERNAL shall be used. */ +#define CPUFREQ_ETERNAL (-1) +#define CPUFREQ_NAME_LEN 16 +/* Print length for names. Extra 1 space for accomodating '\n' in prints */ +#define CPUFREQ_NAME_PLEN (CPUFREQ_NAME_LEN + 1) + struct cpufreq_governor; -/* /sys/devices/system/cpu/cpufreq: entry point for global variables */ -extern struct kobject *cpufreq_global_kobject; -int cpufreq_get_global_kobject(void); -void cpufreq_put_global_kobject(void); -int cpufreq_sysfs_create_file(const struct attribute *attr); -void cpufreq_sysfs_remove_file(const struct attribute *attr); +struct cpufreq_freqs { + unsigned int cpu; /* cpu nr */ + unsigned int old; + unsigned int new; + u8 flags; /* flags of cpufreq_driver, see below. */ +}; -#define CPUFREQ_ETERNAL (-1) struct cpufreq_cpuinfo { unsigned int max_freq; unsigned int min_freq; @@ -117,111 +87,95 @@ struct cpufreq_policy { int transition_ongoing; /* Tracks transition status */ }; -#define CPUFREQ_ADJUST (0) -#define CPUFREQ_INCOMPATIBLE (1) -#define CPUFREQ_NOTIFY (2) -#define CPUFREQ_START (3) -#define CPUFREQ_UPDATE_POLICY_CPU (4) - /* Only for ACPI */ #define CPUFREQ_SHARED_TYPE_NONE (0) /* None */ #define CPUFREQ_SHARED_TYPE_HW (1) /* HW does needed coordination */ #define CPUFREQ_SHARED_TYPE_ALL (2) /* All dependent CPUs should set freq */ #define CPUFREQ_SHARED_TYPE_ANY (3) /* Freq can be set from any dependent CPU*/ +struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu); +void cpufreq_cpu_put(struct cpufreq_policy *data); + static inline bool policy_is_shared(struct cpufreq_policy *policy) { return cpumask_weight(policy->cpus) > 1; } -/******************** cpufreq transition notifiers *******************/ - -#define CPUFREQ_PRECHANGE (0) -#define CPUFREQ_POSTCHANGE (1) -#define CPUFREQ_RESUMECHANGE (8) -#define CPUFREQ_SUSPENDCHANGE (9) +/* /sys/devices/system/cpu/cpufreq: entry point for global variables */ +extern struct kobject *cpufreq_global_kobject; +int cpufreq_get_global_kobject(void); +void cpufreq_put_global_kobject(void); +int cpufreq_sysfs_create_file(const struct attribute *attr); +void cpufreq_sysfs_remove_file(const struct attribute *attr); -struct cpufreq_freqs { - unsigned int cpu; /* cpu nr */ - unsigned int old; - unsigned int new; - u8 flags; /* flags of cpufreq_driver, see below. */ -}; +#ifdef CONFIG_CPU_FREQ +unsigned int cpufreq_get(unsigned int cpu); +unsigned int cpufreq_quick_get(unsigned int cpu); +unsigned int cpufreq_quick_get_max(unsigned int cpu); +void disable_cpufreq(void); -/** - * cpufreq_scale - "old * mult / div" calculation for large values (32-bit-arch - * safe) - * @old: old value - * @div: divisor - * @mult: multiplier - * - * - * new = old * mult / div - */ -static inline unsigned long cpufreq_scale(unsigned long old, u_int div, - u_int mult) +u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy); +int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu); +int cpufreq_update_policy(unsigned int cpu); +bool have_governor_per_policy(void); +struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy); +#else +static inline unsigned int cpufreq_get(unsigned int cpu) { -#if BITS_PER_LONG == 32 - - u64 result = ((u64) old) * ((u64) mult); - do_div(result, div); - return (unsigned long) result; - -#elif BITS_PER_LONG == 64 - - unsigned long result = old * ((u64) mult); - result /= div; - return result; - + return 0; +} +static inline unsigned int cpufreq_quick_get(unsigned int cpu) +{ + return 0; +} +static inline unsigned int cpufreq_quick_get_max(unsigned int cpu) +{ + return 0; +} +static inline void disable_cpufreq(void) { } #endif -}; /********************************************************************* - * CPUFREQ GOVERNORS * + * CPUFREQ DRIVER INTERFACE * *********************************************************************/ -#define CPUFREQ_GOV_START 1 -#define CPUFREQ_GOV_STOP 2 -#define CPUFREQ_GOV_LIMITS 3 -#define CPUFREQ_GOV_POLICY_INIT 4 -#define CPUFREQ_GOV_POLICY_EXIT 5 +#define CPUFREQ_RELATION_L 0 /* lowest frequency at or above target */ +#define CPUFREQ_RELATION_H 1 /* highest frequency below or at target */ -struct cpufreq_governor { - char name[CPUFREQ_NAME_LEN]; - int initialized; - int (*governor) (struct cpufreq_policy *policy, - unsigned int event); - ssize_t (*show_setspeed) (struct cpufreq_policy *policy, - char *buf); - int (*store_setspeed) (struct cpufreq_policy *policy, - unsigned int freq); - unsigned int max_transition_latency; /* HW must be able to switch to - next freq faster than this value in nano secs or we - will fallback to performance governor */ - struct list_head governor_list; - struct module *owner; +struct freq_attr { + struct attribute attr; + ssize_t (*show)(struct cpufreq_policy *, char *); + ssize_t (*store)(struct cpufreq_policy *, const char *, size_t count); }; -/* - * Pass a target to the cpufreq driver. - */ -extern int cpufreq_driver_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation); -extern int __cpufreq_driver_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation); -int cpufreq_register_governor(struct cpufreq_governor *governor); -void cpufreq_unregister_governor(struct cpufreq_governor *governor); +#define cpufreq_freq_attr_ro(_name) \ +static struct freq_attr _name = \ +__ATTR(_name, 0444, show_##_name, NULL) -/********************************************************************* - * CPUFREQ DRIVER INTERFACE * - *********************************************************************/ +#define cpufreq_freq_attr_ro_perm(_name, _perm) \ +static struct freq_attr _name = \ +__ATTR(_name, _perm, show_##_name, NULL) -#define CPUFREQ_RELATION_L 0 /* lowest frequency at or above target */ -#define CPUFREQ_RELATION_H 1 /* highest frequency below or at target */ +#define cpufreq_freq_attr_rw(_name) \ +static struct freq_attr _name = \ +__ATTR(_name, 0644, show_##_name, store_##_name) + +struct global_attr { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, + struct attribute *attr, char *buf); + ssize_t (*store)(struct kobject *a, struct attribute *b, + const char *c, size_t count); +}; + +#define define_one_global_ro(_name) \ +static struct global_attr _name = \ +__ATTR(_name, 0444, show_##_name, NULL) + +#define define_one_global_rw(_name) \ +static struct global_attr _name = \ +__ATTR(_name, 0644, show_##_name, store_##_name) -struct freq_attr; struct cpufreq_driver { struct module *owner; @@ -258,7 +212,6 @@ struct cpufreq_driver { }; /* flags */ - #define CPUFREQ_STICKY 0x01 /* the driver isn't removed even if * all ->init() calls failed */ #define CPUFREQ_CONST_LOOPS 0x02 /* loops_per_jiffy or other kernel @@ -270,8 +223,7 @@ struct cpufreq_driver { int cpufreq_register_driver(struct cpufreq_driver *driver_data); int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); -void cpufreq_notify_transition(struct cpufreq_policy *policy, - struct cpufreq_freqs *freqs, unsigned int state); +const char *cpufreq_get_current_driver(void); static inline void cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned int min, unsigned int max) @@ -289,86 +241,117 @@ static inline void cpufreq_verify_within_limits(struct cpufreq_policy *policy, return; } -struct freq_attr { - struct attribute attr; - ssize_t (*show)(struct cpufreq_policy *, char *); - ssize_t (*store)(struct cpufreq_policy *, const char *, size_t count); -}; - -#define cpufreq_freq_attr_ro(_name) \ -static struct freq_attr _name = \ -__ATTR(_name, 0444, show_##_name, NULL) - -#define cpufreq_freq_attr_ro_perm(_name, _perm) \ -static struct freq_attr _name = \ -__ATTR(_name, _perm, show_##_name, NULL) - -#define cpufreq_freq_attr_rw(_name) \ -static struct freq_attr _name = \ -__ATTR(_name, 0644, show_##_name, store_##_name) +/********************************************************************* + * CPUFREQ NOTIFIER INTERFACE * + *********************************************************************/ -struct global_attr { - struct attribute attr; - ssize_t (*show)(struct kobject *kobj, - struct attribute *attr, char *buf); - ssize_t (*store)(struct kobject *a, struct attribute *b, - const char *c, size_t count); -}; +#define CPUFREQ_TRANSITION_NOTIFIER (0) +#define CPUFREQ_POLICY_NOTIFIER (1) -#define define_one_global_ro(_name) \ -static struct global_attr _name = \ -__ATTR(_name, 0444, show_##_name, NULL) +/* Transition notifiers */ +#define CPUFREQ_PRECHANGE (0) +#define CPUFREQ_POSTCHANGE (1) +#define CPUFREQ_RESUMECHANGE (8) +#define CPUFREQ_SUSPENDCHANGE (9) -#define define_one_global_rw(_name) \ -static struct global_attr _name = \ -__ATTR(_name, 0644, show_##_name, store_##_name) +/* Policy Notifiers */ +#define CPUFREQ_ADJUST (0) +#define CPUFREQ_INCOMPATIBLE (1) +#define CPUFREQ_NOTIFY (2) +#define CPUFREQ_START (3) +#define CPUFREQ_UPDATE_POLICY_CPU (4) -struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu); -void cpufreq_cpu_put(struct cpufreq_policy *data); -const char *cpufreq_get_current_driver(void); +#ifdef CONFIG_CPU_FREQ +int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list); +int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list); -/********************************************************************* - * CPUFREQ 2.6. INTERFACE * - *********************************************************************/ -u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy); -int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu); -int cpufreq_update_policy(unsigned int cpu); -bool have_governor_per_policy(void); -struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy); +void cpufreq_notify_transition(struct cpufreq_policy *policy, + struct cpufreq_freqs *freqs, unsigned int state); -#ifdef CONFIG_CPU_FREQ -/* - * query the current CPU frequency (in kHz). If zero, cpufreq couldn't detect it - */ -unsigned int cpufreq_get(unsigned int cpu); -#else -static inline unsigned int cpufreq_get(unsigned int cpu) +#else /* CONFIG_CPU_FREQ */ +static inline int cpufreq_register_notifier(struct notifier_block *nb, + unsigned int list) { return 0; } -#endif - -/* - * query the last known CPU freq (in kHz). If zero, cpufreq couldn't detect it - */ -#ifdef CONFIG_CPU_FREQ -unsigned int cpufreq_quick_get(unsigned int cpu); -unsigned int cpufreq_quick_get_max(unsigned int cpu); -#else -static inline unsigned int cpufreq_quick_get(unsigned int cpu) +static inline int cpufreq_unregister_notifier(struct notifier_block *nb, + unsigned int list) { return 0; } -static inline unsigned int cpufreq_quick_get_max(unsigned int cpu) +#endif /* !CONFIG_CPU_FREQ */ + +/** + * cpufreq_scale - "old * mult / div" calculation for large values (32-bit-arch + * safe) + * @old: old value + * @div: divisor + * @mult: multiplier + * + * + * new = old * mult / div + */ +static inline unsigned long cpufreq_scale(unsigned long old, u_int div, + u_int mult) { - return 0; -} +#if BITS_PER_LONG == 32 + u64 result = ((u64) old) * ((u64) mult); + do_div(result, div); + return (unsigned long) result; + +#elif BITS_PER_LONG == 64 + unsigned long result = old * ((u64) mult); + result /= div; + return result; #endif +} /********************************************************************* - * CPUFREQ DEFAULT GOVERNOR * + * CPUFREQ GOVERNORS * *********************************************************************/ +/* + * If (cpufreq_driver->target) exists, the ->governor decides what frequency + * within the limits is used. If (cpufreq_driver->setpolicy> exists, these + * two generic policies are available: + */ +#define CPUFREQ_POLICY_POWERSAVE (1) +#define CPUFREQ_POLICY_PERFORMANCE (2) + +/* Governor Events */ +#define CPUFREQ_GOV_START 1 +#define CPUFREQ_GOV_STOP 2 +#define CPUFREQ_GOV_LIMITS 3 +#define CPUFREQ_GOV_POLICY_INIT 4 +#define CPUFREQ_GOV_POLICY_EXIT 5 + +struct cpufreq_governor { + char name[CPUFREQ_NAME_LEN]; + int initialized; + int (*governor) (struct cpufreq_policy *policy, + unsigned int event); + ssize_t (*show_setspeed) (struct cpufreq_policy *policy, + char *buf); + int (*store_setspeed) (struct cpufreq_policy *policy, + unsigned int freq); + unsigned int max_transition_latency; /* HW must be able to switch to + next freq faster than this value in nano secs or we + will fallback to performance governor */ + struct list_head governor_list; + struct module *owner; +}; + +/* Pass a target to the cpufreq driver */ +int cpufreq_driver_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation); +int __cpufreq_driver_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation); +int cpufreq_register_governor(struct cpufreq_governor *governor); +void cpufreq_unregister_governor(struct cpufreq_governor *governor); + +/* CPUFREQ DEFAULT GOVERNOR */ /* * Performance governor is fallback governor if any other gov failed to auto * load due latency restrictions @@ -417,18 +400,16 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, unsigned int relation, unsigned int *index); -/* the following 3 funtions are for cpufreq core use only */ +void cpufreq_frequency_table_update_policy_cpu(struct cpufreq_policy *policy); +ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf); + +/* the following funtion is for cpufreq core use only */ struct cpufreq_frequency_table *cpufreq_frequency_get_table(unsigned int cpu); /* the following are really really optional */ extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs; - void cpufreq_frequency_table_get_attr(struct cpufreq_frequency_table *table, unsigned int cpu); -void cpufreq_frequency_table_update_policy_cpu(struct cpufreq_policy *policy); - void cpufreq_frequency_table_put_attr(unsigned int cpu); -ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf); - #endif /* _LINUX_CPUFREQ_H */ -- cgit v1.2.3-71-gd317 From 3a3e9e06d0c11b8efa95933a88c9e67209fa4330 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 6 Aug 2013 22:53:05 +0530 Subject: cpufreq: Give consistent names to cpufreq_policy objects They are called policy, cur_policy, new_policy, data, etc. Just call them policy wherever possible. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 200 ++++++++++++++++++------------------- drivers/cpufreq/cpufreq_governor.h | 2 +- drivers/cpufreq/cpufreq_ondemand.c | 10 +- drivers/cpufreq/cpufreq_stats.c | 12 +-- include/linux/cpufreq.h | 2 +- 5 files changed, 113 insertions(+), 113 deletions(-) (limited to 'include') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 845837174878..1793fe82595d 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -179,7 +179,7 @@ EXPORT_SYMBOL_GPL(get_cpu_idle_time); static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs) { - struct cpufreq_policy *data; + struct cpufreq_policy *policy; unsigned long flags; if (cpu >= nr_cpu_ids) @@ -195,16 +195,16 @@ static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs) goto err_out_unlock; /* get the CPU */ - data = per_cpu(cpufreq_cpu_data, cpu); + policy = per_cpu(cpufreq_cpu_data, cpu); - if (!data) + if (!policy) goto err_out_put_module; - if (!sysfs && !kobject_get(&data->kobj)) + if (!sysfs && !kobject_get(&policy->kobj)) goto err_out_put_module; read_unlock_irqrestore(&cpufreq_driver_lock, flags); - return data; + return policy; err_out_put_module: module_put(cpufreq_driver->owner); @@ -228,25 +228,25 @@ static struct cpufreq_policy *cpufreq_cpu_get_sysfs(unsigned int cpu) return __cpufreq_cpu_get(cpu, true); } -static void __cpufreq_cpu_put(struct cpufreq_policy *data, bool sysfs) +static void __cpufreq_cpu_put(struct cpufreq_policy *policy, bool sysfs) { if (!sysfs) - kobject_put(&data->kobj); + kobject_put(&policy->kobj); module_put(cpufreq_driver->owner); } -void cpufreq_cpu_put(struct cpufreq_policy *data) +void cpufreq_cpu_put(struct cpufreq_policy *policy) { if (cpufreq_disabled()) return; - __cpufreq_cpu_put(data, false); + __cpufreq_cpu_put(policy, false); } EXPORT_SYMBOL_GPL(cpufreq_cpu_put); -static void cpufreq_cpu_put_sysfs(struct cpufreq_policy *data) +static void cpufreq_cpu_put_sysfs(struct cpufreq_policy *policy) { - __cpufreq_cpu_put(data, true); + __cpufreq_cpu_put(policy, true); } /********************************************************************* @@ -453,8 +453,8 @@ show_one(scaling_min_freq, min); show_one(scaling_max_freq, max); show_one(scaling_cur_freq, cur); -static int __cpufreq_set_policy(struct cpufreq_policy *data, - struct cpufreq_policy *policy); +static int __cpufreq_set_policy(struct cpufreq_policy *policy, + struct cpufreq_policy *new_policy); /** * cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access @@ -1136,7 +1136,7 @@ static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu) CPUFREQ_UPDATE_POLICY_CPU, policy); } -static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *data, +static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy, unsigned int old_cpu, bool frozen) { struct device *cpu_dev; @@ -1144,27 +1144,27 @@ static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *data, int ret; /* first sibling now owns the new sysfs dir */ - cpu_dev = get_cpu_device(cpumask_first(data->cpus)); + cpu_dev = get_cpu_device(cpumask_first(policy->cpus)); /* Don't touch sysfs files during light-weight tear-down */ if (frozen) return cpu_dev->id; sysfs_remove_link(&cpu_dev->kobj, "cpufreq"); - ret = kobject_move(&data->kobj, &cpu_dev->kobj); + ret = kobject_move(&policy->kobj, &cpu_dev->kobj); if (ret) { pr_err("%s: Failed to move kobj: %d", __func__, ret); WARN_ON(lock_policy_rwsem_write(old_cpu)); - cpumask_set_cpu(old_cpu, data->cpus); + cpumask_set_cpu(old_cpu, policy->cpus); write_lock_irqsave(&cpufreq_driver_lock, flags); - per_cpu(cpufreq_cpu_data, old_cpu) = data; + per_cpu(cpufreq_cpu_data, old_cpu) = policy; write_unlock_irqrestore(&cpufreq_driver_lock, flags); unlock_policy_rwsem_write(old_cpu); - ret = sysfs_create_link(&cpu_dev->kobj, &data->kobj, + ret = sysfs_create_link(&cpu_dev->kobj, &policy->kobj, "cpufreq"); return -EINVAL; @@ -1186,7 +1186,7 @@ static int __cpufreq_remove_dev(struct device *dev, unsigned int cpu = dev->id, cpus; int new_cpu; unsigned long flags; - struct cpufreq_policy *data; + struct cpufreq_policy *policy; struct kobject *kobj; struct completion *cmp; @@ -1194,44 +1194,44 @@ static int __cpufreq_remove_dev(struct device *dev, write_lock_irqsave(&cpufreq_driver_lock, flags); - data = per_cpu(cpufreq_cpu_data, cpu); + policy = per_cpu(cpufreq_cpu_data, cpu); per_cpu(cpufreq_cpu_data, cpu) = NULL; /* Save the policy somewhere when doing a light-weight tear-down */ if (frozen) - per_cpu(cpufreq_cpu_data_fallback, cpu) = data; + per_cpu(cpufreq_cpu_data_fallback, cpu) = policy; write_unlock_irqrestore(&cpufreq_driver_lock, flags); - if (!data) { + if (!policy) { pr_debug("%s: No cpu_data found\n", __func__); return -EINVAL; } if (cpufreq_driver->target) - __cpufreq_governor(data, CPUFREQ_GOV_STOP); + __cpufreq_governor(policy, CPUFREQ_GOV_STOP); #ifdef CONFIG_HOTPLUG_CPU if (!cpufreq_driver->setpolicy) strncpy(per_cpu(cpufreq_cpu_governor, cpu), - data->governor->name, CPUFREQ_NAME_LEN); + policy->governor->name, CPUFREQ_NAME_LEN); #endif WARN_ON(lock_policy_rwsem_write(cpu)); - cpus = cpumask_weight(data->cpus); + cpus = cpumask_weight(policy->cpus); if (cpus > 1) - cpumask_clear_cpu(cpu, data->cpus); + cpumask_clear_cpu(cpu, policy->cpus); unlock_policy_rwsem_write(cpu); - if (cpu != data->cpu && !frozen) { + if (cpu != policy->cpu && !frozen) { sysfs_remove_link(&dev->kobj, "cpufreq"); } else if (cpus > 1) { - new_cpu = cpufreq_nominate_new_policy_cpu(data, cpu, frozen); + new_cpu = cpufreq_nominate_new_policy_cpu(policy, cpu, frozen); if (new_cpu >= 0) { WARN_ON(lock_policy_rwsem_write(cpu)); - update_policy_cpu(data, new_cpu); + update_policy_cpu(policy, new_cpu); unlock_policy_rwsem_write(cpu); if (!frozen) { @@ -1244,12 +1244,12 @@ static int __cpufreq_remove_dev(struct device *dev, /* If cpu is last user of policy, free policy */ if (cpus == 1) { if (cpufreq_driver->target) - __cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT); + __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); if (!frozen) { lock_policy_rwsem_read(cpu); - kobj = &data->kobj; - cmp = &data->kobj_unregister; + kobj = &policy->kobj; + cmp = &policy->kobj_unregister; unlock_policy_rwsem_read(cpu); kobject_put(kobj); @@ -1269,14 +1269,14 @@ static int __cpufreq_remove_dev(struct device *dev, * subsequent light-weight ->init() to succeed. */ if (cpufreq_driver->exit) - cpufreq_driver->exit(data); + cpufreq_driver->exit(policy); if (!frozen) - cpufreq_policy_free(data); + cpufreq_policy_free(policy); } else { if (cpufreq_driver->target) { - __cpufreq_governor(data, CPUFREQ_GOV_START); - __cpufreq_governor(data, CPUFREQ_GOV_LIMITS); + __cpufreq_governor(policy, CPUFREQ_GOV_START); + __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); } } @@ -1450,23 +1450,23 @@ static int cpufreq_bp_suspend(void) int ret = 0; int cpu = smp_processor_id(); - struct cpufreq_policy *cpu_policy; + struct cpufreq_policy *policy; pr_debug("suspending cpu %u\n", cpu); /* If there's no policy for the boot CPU, we have nothing to do. */ - cpu_policy = cpufreq_cpu_get(cpu); - if (!cpu_policy) + policy = cpufreq_cpu_get(cpu); + if (!policy) return 0; if (cpufreq_driver->suspend) { - ret = cpufreq_driver->suspend(cpu_policy); + ret = cpufreq_driver->suspend(policy); if (ret) printk(KERN_ERR "cpufreq: suspend failed in ->suspend " - "step on CPU %u\n", cpu_policy->cpu); + "step on CPU %u\n", policy->cpu); } - cpufreq_cpu_put(cpu_policy); + cpufreq_cpu_put(policy); return ret; } @@ -1488,28 +1488,28 @@ static void cpufreq_bp_resume(void) int ret = 0; int cpu = smp_processor_id(); - struct cpufreq_policy *cpu_policy; + struct cpufreq_policy *policy; pr_debug("resuming cpu %u\n", cpu); /* If there's no policy for the boot CPU, we have nothing to do. */ - cpu_policy = cpufreq_cpu_get(cpu); - if (!cpu_policy) + policy = cpufreq_cpu_get(cpu); + if (!policy) return; if (cpufreq_driver->resume) { - ret = cpufreq_driver->resume(cpu_policy); + ret = cpufreq_driver->resume(policy); if (ret) { printk(KERN_ERR "cpufreq: resume failed in ->resume " - "step on CPU %u\n", cpu_policy->cpu); + "step on CPU %u\n", policy->cpu); goto fail; } } - schedule_work(&cpu_policy->update); + schedule_work(&policy->update); fail: - cpufreq_cpu_put(cpu_policy); + cpufreq_cpu_put(policy); } static struct syscore_ops cpufreq_syscore_ops = { @@ -1829,95 +1829,95 @@ EXPORT_SYMBOL(cpufreq_get_policy); * data : current policy. * policy : policy to be set. */ -static int __cpufreq_set_policy(struct cpufreq_policy *data, - struct cpufreq_policy *policy) +static int __cpufreq_set_policy(struct cpufreq_policy *policy, + struct cpufreq_policy *new_policy) { int ret = 0, failed = 1; - pr_debug("setting new policy for CPU %u: %u - %u kHz\n", policy->cpu, - policy->min, policy->max); + pr_debug("setting new policy for CPU %u: %u - %u kHz\n", new_policy->cpu, + new_policy->min, new_policy->max); - memcpy(&policy->cpuinfo, &data->cpuinfo, + memcpy(&new_policy->cpuinfo, &policy->cpuinfo, sizeof(struct cpufreq_cpuinfo)); - if (policy->min > data->max || policy->max < data->min) { + if (new_policy->min > policy->max || new_policy->max < policy->min) { ret = -EINVAL; goto error_out; } /* verify the cpu speed can be set within this limit */ - ret = cpufreq_driver->verify(policy); + ret = cpufreq_driver->verify(new_policy); if (ret) goto error_out; /* adjust if necessary - all reasons */ blocking_notifier_call_chain(&cpufreq_policy_notifier_list, - CPUFREQ_ADJUST, policy); + CPUFREQ_ADJUST, new_policy); /* adjust if necessary - hardware incompatibility*/ blocking_notifier_call_chain(&cpufreq_policy_notifier_list, - CPUFREQ_INCOMPATIBLE, policy); + CPUFREQ_INCOMPATIBLE, new_policy); /* * verify the cpu speed can be set within this limit, which might be * different to the first one */ - ret = cpufreq_driver->verify(policy); + ret = cpufreq_driver->verify(new_policy); if (ret) goto error_out; /* notification of the new policy */ blocking_notifier_call_chain(&cpufreq_policy_notifier_list, - CPUFREQ_NOTIFY, policy); + CPUFREQ_NOTIFY, new_policy); - data->min = policy->min; - data->max = policy->max; + policy->min = new_policy->min; + policy->max = new_policy->max; pr_debug("new min and max freqs are %u - %u kHz\n", - data->min, data->max); + policy->min, policy->max); if (cpufreq_driver->setpolicy) { - data->policy = policy->policy; + policy->policy = new_policy->policy; pr_debug("setting range\n"); - ret = cpufreq_driver->setpolicy(policy); + ret = cpufreq_driver->setpolicy(new_policy); } else { - if (policy->governor != data->governor) { + if (new_policy->governor != policy->governor) { /* save old, working values */ - struct cpufreq_governor *old_gov = data->governor; + struct cpufreq_governor *old_gov = policy->governor; pr_debug("governor switch\n"); /* end old governor */ - if (data->governor) { - __cpufreq_governor(data, CPUFREQ_GOV_STOP); - unlock_policy_rwsem_write(policy->cpu); - __cpufreq_governor(data, + if (policy->governor) { + __cpufreq_governor(policy, CPUFREQ_GOV_STOP); + unlock_policy_rwsem_write(new_policy->cpu); + __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); - lock_policy_rwsem_write(policy->cpu); + lock_policy_rwsem_write(new_policy->cpu); } /* start new governor */ - data->governor = policy->governor; - if (!__cpufreq_governor(data, CPUFREQ_GOV_POLICY_INIT)) { - if (!__cpufreq_governor(data, CPUFREQ_GOV_START)) { + policy->governor = new_policy->governor; + if (!__cpufreq_governor(policy, CPUFREQ_GOV_POLICY_INIT)) { + if (!__cpufreq_governor(policy, CPUFREQ_GOV_START)) { failed = 0; } else { - unlock_policy_rwsem_write(policy->cpu); - __cpufreq_governor(data, + unlock_policy_rwsem_write(new_policy->cpu); + __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); - lock_policy_rwsem_write(policy->cpu); + lock_policy_rwsem_write(new_policy->cpu); } } if (failed) { /* new governor failed, so re-start old one */ pr_debug("starting governor %s failed\n", - data->governor->name); + policy->governor->name); if (old_gov) { - data->governor = old_gov; - __cpufreq_governor(data, + policy->governor = old_gov; + __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_INIT); - __cpufreq_governor(data, + __cpufreq_governor(policy, CPUFREQ_GOV_START); } ret = -EINVAL; @@ -1926,7 +1926,7 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data, /* might be a policy change, too, so fall through */ } pr_debug("governor: change or update limits\n"); - __cpufreq_governor(data, CPUFREQ_GOV_LIMITS); + __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); } error_out: @@ -1942,11 +1942,11 @@ error_out: */ int cpufreq_update_policy(unsigned int cpu) { - struct cpufreq_policy *data = cpufreq_cpu_get(cpu); - struct cpufreq_policy policy; + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + struct cpufreq_policy new_policy; int ret; - if (!data) { + if (!policy) { ret = -ENODEV; goto no_policy; } @@ -1957,34 +1957,34 @@ int cpufreq_update_policy(unsigned int cpu) } pr_debug("updating policy for CPU %u\n", cpu); - memcpy(&policy, data, sizeof(struct cpufreq_policy)); - policy.min = data->user_policy.min; - policy.max = data->user_policy.max; - policy.policy = data->user_policy.policy; - policy.governor = data->user_policy.governor; + memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); + new_policy.min = policy->user_policy.min; + new_policy.max = policy->user_policy.max; + new_policy.policy = policy->user_policy.policy; + new_policy.governor = policy->user_policy.governor; /* * BIOS might change freq behind our back * -> ask driver for current freq and notify governors about a change */ if (cpufreq_driver->get) { - policy.cur = cpufreq_driver->get(cpu); - if (!data->cur) { + new_policy.cur = cpufreq_driver->get(cpu); + if (!policy->cur) { pr_debug("Driver did not initialize current freq"); - data->cur = policy.cur; + policy->cur = new_policy.cur; } else { - if (data->cur != policy.cur && cpufreq_driver->target) - cpufreq_out_of_sync(cpu, data->cur, - policy.cur); + if (policy->cur != new_policy.cur && cpufreq_driver->target) + cpufreq_out_of_sync(cpu, policy->cur, + new_policy.cur); } } - ret = __cpufreq_set_policy(data, &policy); + ret = __cpufreq_set_policy(policy, &new_policy); unlock_policy_rwsem_write(cpu); fail: - cpufreq_cpu_put(data); + cpufreq_cpu_put(policy); no_policy: return ret; } diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h index cf0b7a4425b0..264e509a9149 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -221,7 +221,7 @@ struct od_ops { void (*powersave_bias_init_cpu)(int cpu); unsigned int (*powersave_bias_target)(struct cpufreq_policy *policy, unsigned int freq_next, unsigned int relation); - void (*freq_increase)(struct cpufreq_policy *p, unsigned int freq); + void (*freq_increase)(struct cpufreq_policy *policy, unsigned int freq); }; struct cs_ops { diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 27c732eee431..47d4b69be7c6 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -132,18 +132,18 @@ static void ondemand_powersave_bias_init(void) } } -static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq) +static void dbs_freq_increase(struct cpufreq_policy *policy, unsigned int freq) { - struct dbs_data *dbs_data = p->governor_data; + struct dbs_data *dbs_data = policy->governor_data; struct od_dbs_tuners *od_tuners = dbs_data->tuners; if (od_tuners->powersave_bias) - freq = od_ops.powersave_bias_target(p, freq, + freq = od_ops.powersave_bias_target(policy, freq, CPUFREQ_RELATION_H); - else if (p->cur == p->max) + else if (policy->cur == policy->max) return; - __cpufreq_driver_target(p, freq, od_tuners->powersave_bias ? + __cpufreq_driver_target(policy, freq, od_tuners->powersave_bias ? CPUFREQ_RELATION_L : CPUFREQ_RELATION_H); } diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index a7143b0597af..a17b14ee37d8 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -193,7 +193,7 @@ static int cpufreq_stats_create_table(struct cpufreq_policy *policy, { unsigned int i, j, count = 0, ret = 0; struct cpufreq_stats *stat; - struct cpufreq_policy *data; + struct cpufreq_policy *current_policy; unsigned int alloc_size; unsigned int cpu = policy->cpu; if (per_cpu(cpufreq_stats_table, cpu)) @@ -202,13 +202,13 @@ static int cpufreq_stats_create_table(struct cpufreq_policy *policy, if ((stat) == NULL) return -ENOMEM; - data = cpufreq_cpu_get(cpu); - if (data == NULL) { + current_policy = cpufreq_cpu_get(cpu); + if (current_policy == NULL) { ret = -EINVAL; goto error_get_fail; } - ret = sysfs_create_group(&data->kobj, &stats_attr_group); + ret = sysfs_create_group(¤t_policy->kobj, &stats_attr_group); if (ret) goto error_out; @@ -251,10 +251,10 @@ static int cpufreq_stats_create_table(struct cpufreq_policy *policy, stat->last_time = get_jiffies_64(); stat->last_index = freq_table_get_index(stat, policy->cur); spin_unlock(&cpufreq_stats_lock); - cpufreq_cpu_put(data); + cpufreq_cpu_put(current_policy); return 0; error_out: - cpufreq_cpu_put(data); + cpufreq_cpu_put(current_policy); error_get_fail: kfree(stat); per_cpu(cpufreq_stats_table, cpu) = NULL; diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 0d3b0266bc98..29208923c9d3 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -94,7 +94,7 @@ struct cpufreq_policy { #define CPUFREQ_SHARED_TYPE_ANY (3) /* Freq can be set from any dependent CPU*/ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu); -void cpufreq_cpu_put(struct cpufreq_policy *data); +void cpufreq_cpu_put(struct cpufreq_policy *policy); static inline bool policy_is_shared(struct cpufreq_policy *policy) { -- cgit v1.2.3-71-gd317 From 6261d983f226f0a6a8d4d32b57a032bc23a5ebb6 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 5 Aug 2013 22:51:37 -0700 Subject: ip_tunnel: embed hash list head The IP tunnel hash heads can be embedded in the per-net structure since it is a fixed size. Reduce the size so that the total structure fits in a page size. The original size was overly large, even NETDEV_HASHBITS is only 8 bits! Also, add some white space for readability. Signed-off-by: Stephen Hemminger Acked-by: Pravin B Shelar . Signed-off-by: David S. Miller --- include/net/ip_tunnels.h | 4 ++-- net/ipv4/ip_tunnel.c | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 781b3cf86a2f..c6acd9f8f877 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -86,12 +86,12 @@ struct tnl_ptk_info { #define PACKET_RCVD 0 #define PACKET_REJECT 1 -#define IP_TNL_HASH_BITS 10 +#define IP_TNL_HASH_BITS 7 #define IP_TNL_HASH_SIZE (1 << IP_TNL_HASH_BITS) struct ip_tunnel_net { - struct hlist_head *tunnels; struct net_device *fb_tunnel_dev; + struct hlist_head tunnels[IP_TNL_HASH_SIZE]; }; #ifdef CONFIG_INET diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index ca1cb2d5f6e2..9fdf8a6d95f3 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -838,15 +838,16 @@ int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, { struct ip_tunnel_net *itn = net_generic(net, ip_tnl_net_id); struct ip_tunnel_parm parms; + unsigned int i; - itn->tunnels = kzalloc(IP_TNL_HASH_SIZE * sizeof(struct hlist_head), GFP_KERNEL); - if (!itn->tunnels) - return -ENOMEM; + for (i = 0; i < IP_TNL_HASH_SIZE; i++) + INIT_HLIST_HEAD(&itn->tunnels[i]); if (!ops) { itn->fb_tunnel_dev = NULL; return 0; } + memset(&parms, 0, sizeof(parms)); if (devname) strlcpy(parms.name, devname, IFNAMSIZ); @@ -854,10 +855,9 @@ int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, rtnl_lock(); itn->fb_tunnel_dev = __ip_tunnel_create(net, ops, &parms); rtnl_unlock(); - if (IS_ERR(itn->fb_tunnel_dev)) { - kfree(itn->tunnels); + + if (IS_ERR(itn->fb_tunnel_dev)) return PTR_ERR(itn->fb_tunnel_dev); - } return 0; } @@ -887,7 +887,6 @@ void ip_tunnel_delete_net(struct ip_tunnel_net *itn) ip_tunnel_destroy(itn, &list); unregister_netdevice_many(&list); rtnl_unlock(); - kfree(itn->tunnels); } EXPORT_SYMBOL_GPL(ip_tunnel_delete_net); -- cgit v1.2.3-71-gd317 From b4bf07771faaf959b0a916d35b1b930c030e30a8 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 6 Aug 2013 17:45:03 +0800 Subject: net: move iov_pages() to net/core/iovec.c To let it be reused and reduce code duplication. Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 23 ----------------------- drivers/net/tun.c | 23 ----------------------- include/linux/socket.h | 2 ++ net/core/iovec.c | 24 ++++++++++++++++++++++++ 4 files changed, 26 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index a98fb0ed6aef..dfec20df17ba 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -698,29 +698,6 @@ static int macvtap_skb_to_vnet_hdr(const struct sk_buff *skb, return 0; } -static unsigned long iov_pages(const struct iovec *iv, int offset, - unsigned long nr_segs) -{ - unsigned long seg, base; - int pages = 0, len, size; - - while (nr_segs && (offset >= iv->iov_len)) { - offset -= iv->iov_len; - ++iv; - --nr_segs; - } - - for (seg = 0; seg < nr_segs; seg++) { - base = (unsigned long)iv[seg].iov_base + offset; - len = iv[seg].iov_len - offset; - size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; - pages += size; - offset = 0; - } - - return pages; -} - /* Get packet from user space buffer */ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m, const struct iovec *iv, unsigned long total_len, diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 5dce262f538e..18527ef0cc75 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1041,29 +1041,6 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, return 0; } -static unsigned long iov_pages(const struct iovec *iv, int offset, - unsigned long nr_segs) -{ - unsigned long seg, base; - int pages = 0, len, size; - - while (nr_segs && (offset >= iv->iov_len)) { - offset -= iv->iov_len; - ++iv; - --nr_segs; - } - - for (seg = 0; seg < nr_segs; seg++) { - base = (unsigned long)iv[seg].iov_base + offset; - len = iv[seg].iov_len - offset; - size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; - pages += size; - offset = 0; - } - - return pages; -} - /* Get packet from user space buffer */ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, void *msg_control, const struct iovec *iv, diff --git a/include/linux/socket.h b/include/linux/socket.h index 230c04bda3e2..445ef7519dc2 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -313,6 +313,8 @@ extern int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset, unsigned int len, __wsum *csump); +extern unsigned long iov_pages(const struct iovec *iov, int offset, + unsigned long nr_segs); extern int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode); extern int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata, diff --git a/net/core/iovec.c b/net/core/iovec.c index de178e462682..b77eeecc0011 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -212,3 +212,27 @@ out_fault: goto out; } EXPORT_SYMBOL(csum_partial_copy_fromiovecend); + +unsigned long iov_pages(const struct iovec *iov, int offset, + unsigned long nr_segs) +{ + unsigned long seg, base; + int pages = 0, len, size; + + while (nr_segs && (offset >= iov->iov_len)) { + offset -= iov->iov_len; + ++iov; + --nr_segs; + } + + for (seg = 0; seg < nr_segs; seg++) { + base = (unsigned long)iov[seg].iov_base + offset; + len = iov[seg].iov_len - offset; + size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; + pages += size; + offset = 0; + } + + return pages; +} +EXPORT_SYMBOL(iov_pages); -- cgit v1.2.3-71-gd317 From c3bdeb5c7cc073ccf5ff9624642022a8613a956e Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Tue, 6 Aug 2013 17:45:04 +0800 Subject: net: move zerocopy_sg_from_iovec() to net/core/datagram.c To let it be reused and reduce code duplication. Also document this function. Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 80 ------------------------------------------- drivers/net/tun.c | 80 ------------------------------------------- include/linux/skbuff.h | 4 +++ net/core/datagram.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 160 deletions(-) (limited to 'include') diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index dfec20df17ba..182364abfa35 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -536,86 +536,6 @@ static inline struct sk_buff *macvtap_alloc_skb(struct sock *sk, size_t prepad, return skb; } -/* set skb frags from iovec, this can move to core network code for reuse */ -static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, - int offset, size_t count) -{ - int len = iov_length(from, count) - offset; - int copy = skb_headlen(skb); - int size, offset1 = 0; - int i = 0; - - /* Skip over from offset */ - while (count && (offset >= from->iov_len)) { - offset -= from->iov_len; - ++from; - --count; - } - - /* copy up to skb headlen */ - while (count && (copy > 0)) { - size = min_t(unsigned int, copy, from->iov_len - offset); - if (copy_from_user(skb->data + offset1, from->iov_base + offset, - size)) - return -EFAULT; - if (copy > size) { - ++from; - --count; - offset = 0; - } else - offset += size; - copy -= size; - offset1 += size; - } - - if (len == offset1) - return 0; - - while (count--) { - struct page *page[MAX_SKB_FRAGS]; - int num_pages; - unsigned long base; - unsigned long truesize; - - len = from->iov_len - offset; - if (!len) { - offset = 0; - ++from; - continue; - } - base = (unsigned long)from->iov_base + offset; - size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; - if (i + size > MAX_SKB_FRAGS) - return -EMSGSIZE; - num_pages = get_user_pages_fast(base, size, 0, &page[i]); - if (num_pages != size) { - int j; - - for (j = 0; j < num_pages; j++) - put_page(page[i + j]); - return -EFAULT; - } - truesize = size * PAGE_SIZE; - skb->data_len += len; - skb->len += len; - skb->truesize += truesize; - atomic_add(truesize, &skb->sk->sk_wmem_alloc); - while (len) { - int off = base & ~PAGE_MASK; - int size = min_t(int, len, PAGE_SIZE - off); - __skb_fill_page_desc(skb, i, page[i], off, size); - skb_shinfo(skb)->nr_frags++; - /* increase sk_wmem_alloc */ - base += size; - len -= size; - i++; - } - offset = 0; - ++from; - } - return 0; -} - /* * macvtap_skb_from_vnet_hdr and macvtap_skb_to_vnet_hdr should * be shared with the tun/tap driver. diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 18527ef0cc75..b1630479d4a1 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -961,86 +961,6 @@ static struct sk_buff *tun_alloc_skb(struct tun_file *tfile, return skb; } -/* set skb frags from iovec, this can move to core network code for reuse */ -static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, - int offset, size_t count) -{ - int len = iov_length(from, count) - offset; - int copy = skb_headlen(skb); - int size, offset1 = 0; - int i = 0; - - /* Skip over from offset */ - while (count && (offset >= from->iov_len)) { - offset -= from->iov_len; - ++from; - --count; - } - - /* copy up to skb headlen */ - while (count && (copy > 0)) { - size = min_t(unsigned int, copy, from->iov_len - offset); - if (copy_from_user(skb->data + offset1, from->iov_base + offset, - size)) - return -EFAULT; - if (copy > size) { - ++from; - --count; - offset = 0; - } else - offset += size; - copy -= size; - offset1 += size; - } - - if (len == offset1) - return 0; - - while (count--) { - struct page *page[MAX_SKB_FRAGS]; - int num_pages; - unsigned long base; - unsigned long truesize; - - len = from->iov_len - offset; - if (!len) { - offset = 0; - ++from; - continue; - } - base = (unsigned long)from->iov_base + offset; - size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; - if (i + size > MAX_SKB_FRAGS) - return -EMSGSIZE; - num_pages = get_user_pages_fast(base, size, 0, &page[i]); - if (num_pages != size) { - int j; - - for (j = 0; j < num_pages; j++) - put_page(page[i + j]); - return -EFAULT; - } - truesize = size * PAGE_SIZE; - skb->data_len += len; - skb->len += len; - skb->truesize += truesize; - atomic_add(truesize, &skb->sk->sk_wmem_alloc); - while (len) { - int off = base & ~PAGE_MASK; - int size = min_t(int, len, PAGE_SIZE - off); - __skb_fill_page_desc(skb, i, page[i], off, size); - skb_shinfo(skb)->nr_frags++; - /* increase sk_wmem_alloc */ - base += size; - len -= size; - i++; - } - offset = 0; - ++from; - } - return 0; -} - /* Get packet from user space buffer */ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, void *msg_control, const struct iovec *iv, diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index ae46475d4568..5ac96f31d546 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -2359,6 +2359,10 @@ extern int skb_copy_datagram_from_iovec(struct sk_buff *skb, const struct iovec *from, int from_offset, int len); +extern int zerocopy_sg_from_iovec(struct sk_buff *skb, + const struct iovec *frm, + int offset, + size_t count); extern int skb_copy_datagram_const_iovec(const struct sk_buff *from, int offset, const struct iovec *to, diff --git a/net/core/datagram.c b/net/core/datagram.c index 8ab48cd89559..f987faef21ce 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -573,6 +573,99 @@ fault: } EXPORT_SYMBOL(skb_copy_datagram_from_iovec); +/** + * zerocopy_sg_from_iovec - Build a zerocopy datagram from an iovec + * @skb: buffer to copy + * @from: io vector to copy to + * @offset: offset in the io vector to start copying from + * @count: amount of vectors to copy to buffer from + * + * The function will first copy up to headlen, and then pin the userspace + * pages and build frags through them. + * + * Returns 0, -EFAULT or -EMSGSIZE. + * Note: the iovec is not modified during the copy + */ +int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from, + int offset, size_t count) +{ + int len = iov_length(from, count) - offset; + int copy = skb_headlen(skb); + int size, offset1 = 0; + int i = 0; + + /* Skip over from offset */ + while (count && (offset >= from->iov_len)) { + offset -= from->iov_len; + ++from; + --count; + } + + /* copy up to skb headlen */ + while (count && (copy > 0)) { + size = min_t(unsigned int, copy, from->iov_len - offset); + if (copy_from_user(skb->data + offset1, from->iov_base + offset, + size)) + return -EFAULT; + if (copy > size) { + ++from; + --count; + offset = 0; + } else + offset += size; + copy -= size; + offset1 += size; + } + + if (len == offset1) + return 0; + + while (count--) { + struct page *page[MAX_SKB_FRAGS]; + int num_pages; + unsigned long base; + unsigned long truesize; + + len = from->iov_len - offset; + if (!len) { + offset = 0; + ++from; + continue; + } + base = (unsigned long)from->iov_base + offset; + size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; + if (i + size > MAX_SKB_FRAGS) + return -EMSGSIZE; + num_pages = get_user_pages_fast(base, size, 0, &page[i]); + if (num_pages != size) { + int j; + + for (j = 0; j < num_pages; j++) + put_page(page[i + j]); + return -EFAULT; + } + truesize = size * PAGE_SIZE; + skb->data_len += len; + skb->len += len; + skb->truesize += truesize; + atomic_add(truesize, &skb->sk->sk_wmem_alloc); + while (len) { + int off = base & ~PAGE_MASK; + int size = min_t(int, len, PAGE_SIZE - off); + __skb_fill_page_desc(skb, i, page[i], off, size); + skb_shinfo(skb)->nr_frags++; + /* increase sk_wmem_alloc */ + base += size; + len -= size; + i++; + } + offset = 0; + ++from; + } + return 0; +} +EXPORT_SYMBOL(zerocopy_sg_from_iovec); + static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, u8 __user *to, int len, __wsum *csump) -- cgit v1.2.3-71-gd317 From 72b098964d3c3fb030dcac2d4c869c9851a0d17a Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Tue, 6 Aug 2013 20:32:14 +0100 Subject: video/hdmi: Introduce a generic hdmi_infoframe union MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And a way to pack hdmi_infoframe generically. Cc: Thierry Reding Reviewed-by: Ville Syrjälä Signed-off-by: Damien Lespiau Acked-by: Dave Airlie Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter --- drivers/video/hdmi.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/linux/hdmi.h | 17 +++++++++++++++++ 2 files changed, 60 insertions(+) (limited to 'include') diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index dbd882ffafbb..f7a85e5b7370 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -22,6 +22,7 @@ */ #include +#include #include #include #include @@ -321,3 +322,45 @@ ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, return length; } EXPORT_SYMBOL(hdmi_vendor_infoframe_pack); + +/** + * hdmi_infoframe_pack() - write a HDMI infoframe to binary buffer + * @frame: HDMI infoframe + * @buffer: destination buffer + * @size: size of buffer + * + * Packs the information contained in the @frame structure into a binary + * representation that can be written into the corresponding controller + * registers. Also computes the checksum as required by section 5.3.5 of + * the HDMI 1.4 specification. + * + * Returns the number of bytes packed into the binary buffer or a negative + * error code on failure. + */ +ssize_t +hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size) +{ + ssize_t length; + + switch (frame->any.type) { + case HDMI_INFOFRAME_TYPE_AVI: + length = hdmi_avi_infoframe_pack(&frame->avi, buffer, size); + break; + case HDMI_INFOFRAME_TYPE_SPD: + length = hdmi_spd_infoframe_pack(&frame->spd, buffer, size); + break; + case HDMI_INFOFRAME_TYPE_AUDIO: + length = hdmi_audio_infoframe_pack(&frame->audio, buffer, size); + break; + case HDMI_INFOFRAME_TYPE_VENDOR: + length = hdmi_vendor_infoframe_pack(&frame->vendor, + buffer, size); + break; + default: + WARN(1, "Bad infoframe type %d\n", frame->any.type); + length = -EINVAL; + } + + return length; +} +EXPORT_SYMBOL(hdmi_infoframe_pack); diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index 3b589440ecfe..0f3f82eadef7 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -23,6 +23,12 @@ enum hdmi_infoframe_type { #define HDMI_SPD_INFOFRAME_SIZE 25 #define HDMI_AUDIO_INFOFRAME_SIZE 10 +struct hdmi_any_infoframe { + enum hdmi_infoframe_type type; + unsigned char version; + unsigned char length; +}; + enum hdmi_colorspace { HDMI_COLORSPACE_RGB, HDMI_COLORSPACE_YUV422, @@ -228,4 +234,15 @@ struct hdmi_vendor_infoframe { ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, void *buffer, size_t size); +union hdmi_infoframe { + struct hdmi_any_infoframe any; + struct hdmi_avi_infoframe avi; + struct hdmi_spd_infoframe spd; + struct hdmi_vendor_infoframe vendor; + struct hdmi_audio_infoframe audio; +}; + +ssize_t +hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size); + #endif /* _DRM_HDMI_H */ -- cgit v1.2.3-71-gd317 From 61177b0e12ba162d5de206914e8703d8eb90ad19 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Tue, 6 Aug 2013 20:32:15 +0100 Subject: video/hdmi: Add a macro to return the size of a full infoframe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Thierry Reding Reviewed-by: Ville Syrjälä Signed-off-by: Damien Lespiau Acked-by: Dave Airlie Reviewed-by: Alex Deucher Signed-off-by: Daniel Vetter --- include/linux/hdmi.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index 0f3f82eadef7..bc6743e76e37 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -23,6 +23,9 @@ enum hdmi_infoframe_type { #define HDMI_SPD_INFOFRAME_SIZE 25 #define HDMI_AUDIO_INFOFRAME_SIZE 10 +#define HDMI_INFOFRAME_SIZE(type) \ + (HDMI_INFOFRAME_HEADER_SIZE + HDMI_ ## type ## _INFOFRAME_SIZE) + struct hdmi_any_infoframe { enum hdmi_infoframe_type type; unsigned char version; -- cgit v1.2.3-71-gd317 From 00fd78e5279aec3aa504307ff2db892d3efb555d Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 8 Aug 2013 22:19:12 +0200 Subject: drm: provide agp dummies for CONFIG_AGP=n We currently rely on gcc dead-code elimination so the drm_agp_* helpers are not called if drm_core_has_AGP() is false. That's ugly as hell so provide "static inline" dummies for the case that AGP is disabled. Fixes a build-regression introduced by: commit 28ec711cd427f8b61f73712a43b8100ba8ca933b Author: David Herrmann Date: Sat Jul 27 16:37:00 2013 +0200 drm/agp: move AGP cleanup paths to drm_agpsupport.c v2: switch #ifdef -> #if (spotted by Stephen) Cc: Laurent Pinchart Cc: Daniel Vetter Tested-by: Stephen Warren Signed-off-by: David Herrmann Signed-off-by: Dave Airlie --- include/drm/drmP.h | 49 +---------- include/drm/drm_agpsupport.h | 194 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+), 47 deletions(-) create mode 100644 include/drm/drm_agpsupport.h (limited to 'include') diff --git a/include/drm/drmP.h b/include/drm/drmP.h index fba547368a20..3ecdde6274be 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -62,10 +62,8 @@ #endif #include #include -#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) #include #include -#endif #include #include #include @@ -1226,16 +1224,6 @@ static inline int drm_dev_to_irq(struct drm_device *dev) return dev->driver->bus->get_irq(dev); } - -#if __OS_HAS_AGP -static inline int drm_core_has_AGP(struct drm_device *dev) -{ - return drm_core_check_feature(dev, DRIVER_USE_AGP); -} -#else -#define drm_core_has_AGP(dev) (0) -#endif - #if __OS_HAS_MTRR static inline int drm_core_has_MTRR(struct drm_device *dev) { @@ -1292,14 +1280,6 @@ extern unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait); /* Memory management support (drm_memory.h) */ #include -extern void drm_free_agp(DRM_AGP_MEM * handle, int pages); -extern int drm_bind_agp(DRM_AGP_MEM * handle, unsigned int start); -extern DRM_AGP_MEM *drm_agp_bind_pages(struct drm_device *dev, - struct page **pages, - unsigned long num_pages, - uint32_t gtt_offset, - uint32_t type); -extern int drm_unbind_agp(DRM_AGP_MEM * handle); /* Misc. IOCTL support (drm_ioctl.h) */ extern int drm_irq_by_busid(struct drm_device *dev, void *data, @@ -1453,33 +1433,8 @@ extern int drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv); /* AGP/GART support (drm_agpsupport.h) */ -extern struct drm_agp_head *drm_agp_init(struct drm_device *dev); -extern void drm_agp_destroy(struct drm_agp_head *agp); -extern void drm_agp_clear(struct drm_device *dev); -extern int drm_agp_acquire(struct drm_device *dev); -extern int drm_agp_acquire_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int drm_agp_release(struct drm_device *dev); -extern int drm_agp_release_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int drm_agp_enable(struct drm_device *dev, struct drm_agp_mode mode); -extern int drm_agp_enable_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int drm_agp_info(struct drm_device *dev, struct drm_agp_info *info); -extern int drm_agp_info_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request); -extern int drm_agp_alloc_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request); -extern int drm_agp_free_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request); -extern int drm_agp_unbind_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -extern int drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request); -extern int drm_agp_bind_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); + +#include /* Stub support (drm_stub.h) */ extern int drm_setmaster_ioctl(struct drm_device *dev, void *data, diff --git a/include/drm/drm_agpsupport.h b/include/drm/drm_agpsupport.h new file mode 100644 index 000000000000..a184eeee9c96 --- /dev/null +++ b/include/drm/drm_agpsupport.h @@ -0,0 +1,194 @@ +#ifndef _DRM_AGPSUPPORT_H_ +#define _DRM_AGPSUPPORT_H_ + +#include +#include +#include +#include +#include +#include + +#if __OS_HAS_AGP + +void drm_free_agp(DRM_AGP_MEM * handle, int pages); +int drm_bind_agp(DRM_AGP_MEM * handle, unsigned int start); +int drm_unbind_agp(DRM_AGP_MEM * handle); +DRM_AGP_MEM *drm_agp_bind_pages(struct drm_device *dev, + struct page **pages, + unsigned long num_pages, + uint32_t gtt_offset, + uint32_t type); + +struct drm_agp_head *drm_agp_init(struct drm_device *dev); +void drm_agp_destroy(struct drm_agp_head *agp); +void drm_agp_clear(struct drm_device *dev); +int drm_agp_acquire(struct drm_device *dev); +int drm_agp_acquire_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_agp_release(struct drm_device *dev); +int drm_agp_release_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_agp_enable(struct drm_device *dev, struct drm_agp_mode mode); +int drm_agp_enable_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_agp_info(struct drm_device *dev, struct drm_agp_info *info); +int drm_agp_info_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request); +int drm_agp_alloc_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request); +int drm_agp_free_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request); +int drm_agp_unbind_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request); +int drm_agp_bind_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +static inline int drm_core_has_AGP(struct drm_device *dev) +{ + return drm_core_check_feature(dev, DRIVER_USE_AGP); +} + +#else /* __OS_HAS_AGP */ + +static inline void drm_free_agp(DRM_AGP_MEM * handle, int pages) +{ +} + +static inline int drm_bind_agp(DRM_AGP_MEM * handle, unsigned int start) +{ + return -ENODEV; +} + +static inline int drm_unbind_agp(DRM_AGP_MEM * handle) +{ + return -ENODEV; +} + +static inline DRM_AGP_MEM *drm_agp_bind_pages(struct drm_device *dev, + struct page **pages, + unsigned long num_pages, + uint32_t gtt_offset, + uint32_t type) +{ + return NULL; +} + +static inline struct drm_agp_head *drm_agp_init(struct drm_device *dev) +{ + return NULL; +} + +static inline void drm_agp_destroy(struct drm_agp_head *agp) +{ +} + +static inline void drm_agp_clear(struct drm_device *dev) +{ +} + +static inline int drm_agp_acquire(struct drm_device *dev) +{ + return -ENODEV; +} + +static inline int drm_agp_acquire_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int drm_agp_release(struct drm_device *dev) +{ + return -ENODEV; +} + +static inline int drm_agp_release_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int drm_agp_enable(struct drm_device *dev, + struct drm_agp_mode mode) +{ + return -ENODEV; +} + +static inline int drm_agp_enable_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int drm_agp_info(struct drm_device *dev, + struct drm_agp_info *info) +{ + return -ENODEV; +} + +static inline int drm_agp_info_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int drm_agp_alloc(struct drm_device *dev, + struct drm_agp_buffer *request) +{ + return -ENODEV; +} + +static inline int drm_agp_alloc_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int drm_agp_free(struct drm_device *dev, + struct drm_agp_buffer *request) +{ + return -ENODEV; +} + +static inline int drm_agp_free_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int drm_agp_unbind(struct drm_device *dev, + struct drm_agp_binding *request) +{ + return -ENODEV; +} + +static inline int drm_agp_unbind_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int drm_agp_bind(struct drm_device *dev, + struct drm_agp_binding *request) +{ + return -ENODEV; +} + +static inline int drm_agp_bind_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return -ENODEV; +} + +static inline int drm_core_has_AGP(struct drm_device *dev) +{ + return 0; +} + +#endif /* __OS_HAS_AGP */ + +#endif /* _DRM_AGPSUPPORT_H_ */ -- cgit v1.2.3-71-gd317 From 8af01f56a03e9cbd91a55d688fce1315021efba8 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:22 -0400 Subject: cgroup: s/cgroup_subsys_state/cgroup_css/ s/task_subsys_state/task_css/ The names of the two struct cgroup_subsys_state accessors - cgroup_subsys_state() and task_subsys_state() - are somewhat awkward. The former clashes with the type name and the latter doesn't even indicate it's somehow related to cgroup. We're about to revamp large portion of cgroup API, so, let's rename them so that they're less awkward. Most per-controller usages of the accessors are localized in accessor wrappers and given the amount of scheduled changes, this isn't gonna add any noticeable headache. Rename cgroup_subsys_state() to cgroup_css() and task_subsys_state() to task_css(). This patch is pure rename. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- block/blk-cgroup.h | 5 ++--- fs/bio.c | 2 +- include/linux/cgroup.h | 31 +++++++++++++++++++------------ include/net/cls_cgroup.h | 4 ++-- include/net/netprio_cgroup.h | 4 ++-- kernel/cgroup.c | 2 +- kernel/cgroup_freezer.c | 4 ++-- kernel/cpuset.c | 6 +++--- kernel/events/core.c | 6 +++--- kernel/sched/core.c | 4 ++-- kernel/sched/cpuacct.c | 4 ++-- kernel/sched/sched.h | 6 +++--- mm/hugetlb_cgroup.c | 6 ++---- mm/memcontrol.c | 5 ++--- mm/vmpressure.c | 2 +- net/core/netprio_cgroup.c | 2 +- net/sched/cls_cgroup.c | 4 ++-- security/device_cgroup.c | 4 ++-- 18 files changed, 52 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 8056c03a3382..628e50f6f8a8 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -181,14 +181,13 @@ void blkg_conf_finish(struct blkg_conf_ctx *ctx); static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) { - return container_of(cgroup_subsys_state(cgroup, blkio_subsys_id), + return container_of(cgroup_css(cgroup, blkio_subsys_id), struct blkcg, css); } static inline struct blkcg *task_blkcg(struct task_struct *tsk) { - return container_of(task_subsys_state(tsk, blkio_subsys_id), - struct blkcg, css); + return container_of(task_css(tsk, blkio_subsys_id), struct blkcg, css); } static inline struct blkcg *bio_blkcg(struct bio *bio) diff --git a/fs/bio.c b/fs/bio.c index 94bbc04dba77..8e0348f6e5bd 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -1946,7 +1946,7 @@ int bio_associate_current(struct bio *bio) /* associate blkcg if exists */ rcu_read_lock(); - css = task_subsys_state(current, blkio_subsys_id); + css = task_css(current, blkio_subsys_id); if (css && css_tryget(css)) bio->bi_css = css; rcu_read_unlock(); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 44dd422d7e9b..552c5feef733 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -647,8 +647,15 @@ struct cgroup_subsys { #undef IS_SUBSYS_ENABLED #undef SUBSYS -static inline struct cgroup_subsys_state *cgroup_subsys_state( - struct cgroup *cgrp, int subsys_id) +/** + * cgroup_css - obtain a cgroup's css for the specified subsystem + * @cgrp: the cgroup of interest + * @subsys_id: the subsystem of interest + * + * Return @cgrp's css (cgroup_subsys_state) associated with @subsys_id. + */ +static inline struct cgroup_subsys_state *cgroup_css(struct cgroup *cgrp, + int subsys_id) { return cgrp->subsys[subsys_id]; } @@ -678,7 +685,7 @@ extern struct mutex cgroup_mutex; #endif /** - * task_subsys_state_check - obtain css for (task, subsys) w/ extra access conds + * task_css_check - obtain css for (task, subsys) w/ extra access conds * @task: the target task * @subsys_id: the target subsystem ID * @__c: extra condition expression to be passed to rcu_dereference_check() @@ -686,7 +693,7 @@ extern struct mutex cgroup_mutex; * Return the cgroup_subsys_state for the (@task, @subsys_id) pair. The * synchronization rules are the same as task_css_set_check(). */ -#define task_subsys_state_check(task, subsys_id, __c) \ +#define task_css_check(task, subsys_id, __c) \ task_css_set_check((task), (__c))->subsys[(subsys_id)] /** @@ -701,22 +708,22 @@ static inline struct css_set *task_css_set(struct task_struct *task) } /** - * task_subsys_state - obtain css for (task, subsys) + * task_css - obtain css for (task, subsys) * @task: the target task * @subsys_id: the target subsystem ID * - * See task_subsys_state_check(). + * See task_css_check(). */ -static inline struct cgroup_subsys_state * -task_subsys_state(struct task_struct *task, int subsys_id) +static inline struct cgroup_subsys_state *task_css(struct task_struct *task, + int subsys_id) { - return task_subsys_state_check(task, subsys_id, false); + return task_css_check(task, subsys_id, false); } -static inline struct cgroup* task_cgroup(struct task_struct *task, - int subsys_id) +static inline struct cgroup *task_cgroup(struct task_struct *task, + int subsys_id) { - return task_subsys_state(task, subsys_id)->cgroup; + return task_css(task, subsys_id)->cgroup; } /** diff --git a/include/net/cls_cgroup.h b/include/net/cls_cgroup.h index 0fee0617fb7d..52adaa75dac9 100644 --- a/include/net/cls_cgroup.h +++ b/include/net/cls_cgroup.h @@ -35,7 +35,7 @@ static inline u32 task_cls_classid(struct task_struct *p) return 0; rcu_read_lock(); - classid = container_of(task_subsys_state(p, net_cls_subsys_id), + classid = container_of(task_css(p, net_cls_subsys_id), struct cgroup_cls_state, css)->classid; rcu_read_unlock(); @@ -51,7 +51,7 @@ static inline u32 task_cls_classid(struct task_struct *p) return 0; rcu_read_lock(); - css = task_subsys_state(p, net_cls_subsys_id); + css = task_css(p, net_cls_subsys_id); if (css) classid = container_of(css, struct cgroup_cls_state, css)->classid; diff --git a/include/net/netprio_cgroup.h b/include/net/netprio_cgroup.h index 50ab8c26ab59..8110fa7ae60a 100644 --- a/include/net/netprio_cgroup.h +++ b/include/net/netprio_cgroup.h @@ -39,7 +39,7 @@ static inline u32 task_netprioidx(struct task_struct *p) u32 idx; rcu_read_lock(); - css = task_subsys_state(p, net_prio_subsys_id); + css = task_css(p, net_prio_subsys_id); idx = css->cgroup->id; rcu_read_unlock(); return idx; @@ -53,7 +53,7 @@ static inline u32 task_netprioidx(struct task_struct *p) u32 idx = 0; rcu_read_lock(); - css = task_subsys_state(p, net_prio_subsys_id); + css = task_css(p, net_prio_subsys_id); if (css) idx = css->cgroup->id; rcu_read_unlock(); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index ae4c46834633..0b3caa3220cb 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -81,7 +81,7 @@ */ #ifdef CONFIG_PROVE_RCU DEFINE_MUTEX(cgroup_mutex); -EXPORT_SYMBOL_GPL(cgroup_mutex); /* only for task_subsys_state_check() */ +EXPORT_SYMBOL_GPL(cgroup_mutex); /* only for lockdep */ #else static DEFINE_MUTEX(cgroup_mutex); #endif diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 75dda1ea5026..9d3f61566fec 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -47,13 +47,13 @@ struct freezer { static inline struct freezer *cgroup_freezer(struct cgroup *cgroup) { - return container_of(cgroup_subsys_state(cgroup, freezer_subsys_id), + return container_of(cgroup_css(cgroup, freezer_subsys_id), struct freezer, css); } static inline struct freezer *task_freezer(struct task_struct *task) { - return container_of(task_subsys_state(task, freezer_subsys_id), + return container_of(task_css(task, freezer_subsys_id), struct freezer, css); } diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 1b9c31549797..be4512ba2c0c 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -117,14 +117,14 @@ struct cpuset { /* Retrieve the cpuset for a cgroup */ static inline struct cpuset *cgroup_cs(struct cgroup *cgrp) { - return container_of(cgroup_subsys_state(cgrp, cpuset_subsys_id), + return container_of(cgroup_css(cgrp, cpuset_subsys_id), struct cpuset, css); } /* Retrieve the cpuset for a task */ static inline struct cpuset *task_cs(struct task_struct *task) { - return container_of(task_subsys_state(task, cpuset_subsys_id), + return container_of(task_css(task, cpuset_subsys_id), struct cpuset, css); } @@ -2724,7 +2724,7 @@ int proc_cpuset_show(struct seq_file *m, void *unused_v) goto out_free; rcu_read_lock(); - css = task_subsys_state(tsk, cpuset_subsys_id); + css = task_css(tsk, cpuset_subsys_id); retval = cgroup_path(css->cgroup, buf, PAGE_SIZE); rcu_read_unlock(); if (retval < 0) diff --git a/kernel/events/core.c b/kernel/events/core.c index 1833bc5a84a7..414c61f4d776 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -340,8 +340,8 @@ struct perf_cgroup { static inline struct perf_cgroup * perf_cgroup_from_task(struct task_struct *task) { - return container_of(task_subsys_state(task, perf_subsys_id), - struct perf_cgroup, css); + return container_of(task_css(task, perf_subsys_id), + struct perf_cgroup, css); } static inline bool @@ -7798,7 +7798,7 @@ static struct cgroup_subsys_state *perf_cgroup_css_alloc(struct cgroup *cont) static void perf_cgroup_css_free(struct cgroup *cont) { struct perf_cgroup *jc; - jc = container_of(cgroup_subsys_state(cont, perf_subsys_id), + jc = container_of(cgroup_css(cont, perf_subsys_id), struct perf_cgroup, css); free_percpu(jc->info); kfree(jc); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 9b1f2e533b95..323d907eac1a 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6761,7 +6761,7 @@ void sched_move_task(struct task_struct *tsk) if (unlikely(running)) tsk->sched_class->put_prev_task(rq, tsk); - tg = container_of(task_subsys_state_check(tsk, cpu_cgroup_subsys_id, + tg = container_of(task_css_check(tsk, cpu_cgroup_subsys_id, lockdep_is_held(&tsk->sighand->siglock)), struct task_group, css); tg = autogroup_task_group(tsk, tg); @@ -7086,7 +7086,7 @@ int sched_rt_handler(struct ctl_table *table, int write, /* return corresponding task_group object of a cgroup */ static inline struct task_group *cgroup_tg(struct cgroup *cgrp) { - return container_of(cgroup_subsys_state(cgrp, cpu_cgroup_subsys_id), + return container_of(cgroup_css(cgrp, cpu_cgroup_subsys_id), struct task_group, css); } diff --git a/kernel/sched/cpuacct.c b/kernel/sched/cpuacct.c index dbb7e2cd95eb..4a210faaab77 100644 --- a/kernel/sched/cpuacct.c +++ b/kernel/sched/cpuacct.c @@ -36,14 +36,14 @@ struct cpuacct { /* return cpu accounting group corresponding to this container */ static inline struct cpuacct *cgroup_ca(struct cgroup *cgrp) { - return container_of(cgroup_subsys_state(cgrp, cpuacct_subsys_id), + return container_of(cgroup_css(cgrp, cpuacct_subsys_id), struct cpuacct, css); } /* return cpu accounting group to which this task belongs */ static inline struct cpuacct *task_ca(struct task_struct *tsk) { - return container_of(task_subsys_state(tsk, cpuacct_subsys_id), + return container_of(task_css(tsk, cpuacct_subsys_id), struct cpuacct, css); } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index ef0a7b2439dd..471a56db05ea 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -665,9 +665,9 @@ extern int group_balance_cpu(struct sched_group *sg); /* * Return the group to which this tasks belongs. * - * We cannot use task_subsys_state() and friends because the cgroup - * subsystem changes that value before the cgroup_subsys::attach() method - * is called, therefore we cannot pin it and might observe the wrong value. + * We cannot use task_css() and friends because the cgroup subsystem + * changes that value before the cgroup_subsys::attach() method is called, + * therefore we cannot pin it and might observe the wrong value. * * The same is true for autogroup's p->signal->autogroup->tg, the autogroup * core changes this before calling sched_move_task(). diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index 9cea7de22ffb..50f213fc52c7 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -42,15 +42,13 @@ struct hugetlb_cgroup *hugetlb_cgroup_from_css(struct cgroup_subsys_state *s) static inline struct hugetlb_cgroup *hugetlb_cgroup_from_cgroup(struct cgroup *cgroup) { - return hugetlb_cgroup_from_css(cgroup_subsys_state(cgroup, - hugetlb_subsys_id)); + return hugetlb_cgroup_from_css(cgroup_css(cgroup, hugetlb_subsys_id)); } static inline struct hugetlb_cgroup *hugetlb_cgroup_from_task(struct task_struct *task) { - return hugetlb_cgroup_from_css(task_subsys_state(task, - hugetlb_subsys_id)); + return hugetlb_cgroup_from_css(task_css(task, hugetlb_subsys_id)); } static inline bool hugetlb_cgroup_is_root(struct hugetlb_cgroup *h_cg) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index d12ca6f3c293..b47bd3ad3c2b 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1037,8 +1037,7 @@ static void memcg_check_events(struct mem_cgroup *memcg, struct page *page) struct mem_cgroup *mem_cgroup_from_cont(struct cgroup *cont) { - return mem_cgroup_from_css( - cgroup_subsys_state(cont, mem_cgroup_subsys_id)); + return mem_cgroup_from_css(cgroup_css(cont, mem_cgroup_subsys_id)); } struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p) @@ -1051,7 +1050,7 @@ struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p) if (unlikely(!p)) return NULL; - return mem_cgroup_from_css(task_subsys_state(p, mem_cgroup_subsys_id)); + return mem_cgroup_from_css(task_css(p, mem_cgroup_subsys_id)); } struct mem_cgroup *try_get_mem_cgroup_from_mm(struct mm_struct *mm) diff --git a/mm/vmpressure.c b/mm/vmpressure.c index 736a6011c2c8..7f1654d3cec7 100644 --- a/mm/vmpressure.c +++ b/mm/vmpressure.c @@ -76,7 +76,7 @@ static struct vmpressure *work_to_vmpressure(struct work_struct *work) static struct vmpressure *cg_to_vmpressure(struct cgroup *cg) { - return css_to_vmpressure(cgroup_subsys_state(cg, mem_cgroup_subsys_id)); + return css_to_vmpressure(cgroup_css(cg, mem_cgroup_subsys_id)); } static struct vmpressure *vmpressure_parent(struct vmpressure *vmpr) diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index e533259dce3c..ccf852311987 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -31,7 +31,7 @@ static inline struct cgroup_netprio_state *cgrp_netprio_state(struct cgroup *cgrp) { - return container_of(cgroup_subsys_state(cgrp, net_prio_subsys_id), + return container_of(cgroup_css(cgrp, net_prio_subsys_id), struct cgroup_netprio_state, css); } diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 3a294eb98d61..5ee72a001df0 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -25,13 +25,13 @@ static inline struct cgroup_cls_state *cgrp_cls_state(struct cgroup *cgrp) { - return container_of(cgroup_subsys_state(cgrp, net_cls_subsys_id), + return container_of(cgroup_css(cgrp, net_cls_subsys_id), struct cgroup_cls_state, css); } static inline struct cgroup_cls_state *task_cls_state(struct task_struct *p) { - return container_of(task_subsys_state(p, net_cls_subsys_id), + return container_of(task_css(p, net_cls_subsys_id), struct cgroup_cls_state, css); } diff --git a/security/device_cgroup.c b/security/device_cgroup.c index e8aad69f0d69..87a0a037fbd6 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -58,12 +58,12 @@ static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) static inline struct dev_cgroup *cgroup_to_devcgroup(struct cgroup *cgroup) { - return css_to_devcgroup(cgroup_subsys_state(cgroup, devices_subsys_id)); + return css_to_devcgroup(cgroup_css(cgroup, devices_subsys_id)); } static inline struct dev_cgroup *task_devcgroup(struct task_struct *task) { - return css_to_devcgroup(task_subsys_state(task, devices_subsys_id)); + return css_to_devcgroup(task_css(task, devices_subsys_id)); } struct cgroup_subsys devices_subsys; -- cgit v1.2.3-71-gd317 From 6d37b97428d20a21ffc39ba90e97e91e2a79a986 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:22 -0400 Subject: netprio_cgroup: pass around @css instead of @cgroup and kill struct cgroup_netprio_state cgroup controller API will be converted to primarily use struct cgroup_subsys_state instead of struct cgroup. In preparation, make the internal functions of netprio_cgroup pass around @css instead of @cgrp. While at it, kill struct cgroup_netprio_state which only contained struct cgroup_subsys_state without serving any purpose. All functions are converted to deal with @css directly. This patch shouldn't cause any behavior differences. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Neil Horman Acked-by: David S. Miller --- include/net/netprio_cgroup.h | 4 ---- net/core/netprio_cgroup.c | 56 ++++++++++++++++++++++---------------------- 2 files changed, 28 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/net/netprio_cgroup.h b/include/net/netprio_cgroup.h index 8110fa7ae60a..a24f8bb3ca47 100644 --- a/include/net/netprio_cgroup.h +++ b/include/net/netprio_cgroup.h @@ -25,10 +25,6 @@ struct netprio_map { u32 priomap[]; }; -struct cgroup_netprio_state { - struct cgroup_subsys_state css; -}; - extern void sock_update_netprioidx(struct sock *sk); #if IS_BUILTIN(CONFIG_NETPRIO_CGROUP) diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index ccf852311987..5dfac8886e12 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -29,12 +29,6 @@ #define PRIOMAP_MIN_SZ 128 -static inline struct cgroup_netprio_state *cgrp_netprio_state(struct cgroup *cgrp) -{ - return container_of(cgroup_css(cgrp, net_prio_subsys_id), - struct cgroup_netprio_state, css); -} - /* * Extend @dev->priomap so that it's large enough to accomodate * @target_idx. @dev->priomap.priomap_len > @target_idx after successful @@ -87,68 +81,72 @@ static int extend_netdev_table(struct net_device *dev, u32 target_idx) /** * netprio_prio - return the effective netprio of a cgroup-net_device pair - * @cgrp: cgroup part of the target pair + * @css: css part of the target pair * @dev: net_device part of the target pair * * Should be called under RCU read or rtnl lock. */ -static u32 netprio_prio(struct cgroup *cgrp, struct net_device *dev) +static u32 netprio_prio(struct cgroup_subsys_state *css, struct net_device *dev) { struct netprio_map *map = rcu_dereference_rtnl(dev->priomap); + int id = css->cgroup->id; - if (map && cgrp->id < map->priomap_len) - return map->priomap[cgrp->id]; + if (map && id < map->priomap_len) + return map->priomap[id]; return 0; } /** * netprio_set_prio - set netprio on a cgroup-net_device pair - * @cgrp: cgroup part of the target pair + * @css: css part of the target pair * @dev: net_device part of the target pair * @prio: prio to set * - * Set netprio to @prio on @cgrp-@dev pair. Should be called under rtnl + * Set netprio to @prio on @css-@dev pair. Should be called under rtnl * lock and may fail under memory pressure for non-zero @prio. */ -static int netprio_set_prio(struct cgroup *cgrp, struct net_device *dev, - u32 prio) +static int netprio_set_prio(struct cgroup_subsys_state *css, + struct net_device *dev, u32 prio) { struct netprio_map *map; + int id = css->cgroup->id; int ret; /* avoid extending priomap for zero writes */ map = rtnl_dereference(dev->priomap); - if (!prio && (!map || map->priomap_len <= cgrp->id)) + if (!prio && (!map || map->priomap_len <= id)) return 0; - ret = extend_netdev_table(dev, cgrp->id); + ret = extend_netdev_table(dev, id); if (ret) return ret; map = rtnl_dereference(dev->priomap); - map->priomap[cgrp->id] = prio; + map->priomap[id] = prio; return 0; } static struct cgroup_subsys_state *cgrp_css_alloc(struct cgroup *cgrp) { - struct cgroup_netprio_state *cs; + struct cgroup_subsys_state *css; - cs = kzalloc(sizeof(*cs), GFP_KERNEL); - if (!cs) + css = kzalloc(sizeof(*css), GFP_KERNEL); + if (!css) return ERR_PTR(-ENOMEM); - return &cs->css; + return css; } static int cgrp_css_online(struct cgroup *cgrp) { - struct cgroup *parent = cgrp->parent; + struct cgroup_subsys_state *css = cgroup_css(cgrp, net_prio_subsys_id); + struct cgroup_subsys_state *parent_css; struct net_device *dev; int ret = 0; - if (!parent) + if (!cgrp->parent) return 0; + parent_css = cgroup_css(cgrp->parent, net_prio_subsys_id); rtnl_lock(); /* @@ -156,9 +154,9 @@ static int cgrp_css_online(struct cgroup *cgrp) * onlining, there is no need to clear them on offline. */ for_each_netdev(&init_net, dev) { - u32 prio = netprio_prio(parent, dev); + u32 prio = netprio_prio(parent_css, dev); - ret = netprio_set_prio(cgrp, dev, prio); + ret = netprio_set_prio(css, dev, prio); if (ret) break; } @@ -168,7 +166,7 @@ static int cgrp_css_online(struct cgroup *cgrp) static void cgrp_css_free(struct cgroup *cgrp) { - kfree(cgrp_netprio_state(cgrp)); + kfree(cgroup_css(cgrp, net_prio_subsys_id)); } static u64 read_prioidx(struct cgroup *cgrp, struct cftype *cft) @@ -179,11 +177,12 @@ static u64 read_prioidx(struct cgroup *cgrp, struct cftype *cft) static int read_priomap(struct cgroup *cont, struct cftype *cft, struct cgroup_map_cb *cb) { + struct cgroup_subsys_state *css = cgroup_css(cont, net_prio_subsys_id); struct net_device *dev; rcu_read_lock(); for_each_netdev_rcu(&init_net, dev) - cb->fill(cb, dev->name, netprio_prio(cont, dev)); + cb->fill(cb, dev->name, netprio_prio(css, dev)); rcu_read_unlock(); return 0; } @@ -191,6 +190,7 @@ static int read_priomap(struct cgroup *cont, struct cftype *cft, static int write_priomap(struct cgroup *cgrp, struct cftype *cft, const char *buffer) { + struct cgroup_subsys_state *css = cgroup_css(cgrp, net_prio_subsys_id); char devname[IFNAMSIZ + 1]; struct net_device *dev; u32 prio; @@ -205,7 +205,7 @@ static int write_priomap(struct cgroup *cgrp, struct cftype *cft, rtnl_lock(); - ret = netprio_set_prio(cgrp, dev, prio); + ret = netprio_set_prio(css, dev, prio); rtnl_unlock(); dev_put(dev); -- cgit v1.2.3-71-gd317 From 72c97e54e0f043d33b246d7460ae0a36c4b8c643 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:22 -0400 Subject: cgroup: add subsystem pointer to cgroup_subsys_state Currently, given a cgroup_subsys_state, there's no way to find out which subsystem the css is for, which we'll need to convert the cgroup controller API to primarily use @css instead of @cgroup. This patch adds cgroup_subsys_state->ss which points to the subsystem the @css belongs to. While at it, remove the comment about accessing @css->cgroup to determine the hierarchy. cgroup core will provide API to traverse hierarchy of css'es and we don't want subsystems to directly walk cgroup hierarchies anymore. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 9 ++++----- kernel/cgroup.c | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 552c5feef733..821678aae4db 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -66,13 +66,12 @@ enum cgroup_subsys_id { /* Per-subsystem/per-cgroup state maintained by the system. */ struct cgroup_subsys_state { - /* - * The cgroup that this subsystem is attached to. Useful - * for subsystems that want to know about the cgroup - * hierarchy structure - */ + /* the cgroup that this css is attached to */ struct cgroup *cgroup; + /* the cgroup subsystem that this css is attached to */ + struct cgroup_subsys *ss; + /* reference count - access via css_[try]get() and css_put() */ struct percpu_ref refcnt; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 0b3caa3220cb..4234428f1014 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4186,6 +4186,7 @@ static void init_cgroup_css(struct cgroup_subsys_state *css, struct cgroup *cgrp) { css->cgroup = cgrp; + css->ss = ss; css->flags = 0; css->id = NULL; if (cgrp == cgroup_dummy_top) -- cgit v1.2.3-71-gd317 From 6387698699afd72d6304566fb6ccf84bffe07c56 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:23 -0400 Subject: cgroup: add css_parent() Currently, controllers have to explicitly follow the cgroup hierarchy to find the parent of a given css. cgroup is moving towards using cgroup_subsys_state as the main controller interface construct, so let's provide a way to climb the hierarchy using just csses. This patch implements css_parent() which, given a css, returns its parent. The function is guarnateed to valid non-NULL parent css as long as the target css is not at the top of the hierarchy. freezer, cpuset, cpu, cpuacct, hugetlb, memory, net_cls and devices are converted to use css_parent() instead of accessing cgroup->parent directly. * __parent_ca() is dropped from cpuacct and its usage is replaced with parent_ca(). The only difference between the two was NULL test on cgroup->parent which is now embedded in css_parent() making the distinction moot. Note that eventually a css->parent field will be added to css and the NULL check in css_parent() will go away. This patch shouldn't cause any behavior differences. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- block/blk-cgroup.h | 4 +--- include/linux/cgroup.h | 15 +++++++++++++++ kernel/cgroup_freezer.c | 8 ++------ kernel/cpuset.c | 6 +----- kernel/sched/core.c | 9 +++------ kernel/sched/cpuacct.c | 11 ++--------- mm/hugetlb_cgroup.c | 6 +----- mm/memcontrol.c | 39 +++++++++++---------------------------- net/sched/cls_cgroup.c | 8 +++++--- security/device_cgroup.c | 18 +++++------------- 10 files changed, 46 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 8e5863e900bf..b6802c46d68f 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -209,9 +209,7 @@ static inline struct blkcg *bio_blkcg(struct bio *bio) */ static inline struct blkcg *blkcg_parent(struct blkcg *blkcg) { - struct cgroup *pcg = blkcg->css.cgroup->parent; - - return pcg ? cgroup_to_blkcg(pcg) : NULL; + return css_to_blkcg(css_parent(&blkcg->css)); } /** diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 821678aae4db..18112a3bb12b 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -646,6 +646,21 @@ struct cgroup_subsys { #undef IS_SUBSYS_ENABLED #undef SUBSYS +/** + * css_parent - find the parent css + * @css: the target cgroup_subsys_state + * + * Return the parent css of @css. This function is guaranteed to return + * non-NULL parent as long as @css isn't the root. + */ +static inline +struct cgroup_subsys_state *css_parent(struct cgroup_subsys_state *css) +{ + struct cgroup *parent_cgrp = css->cgroup->parent; + + return parent_cgrp ? parent_cgrp->subsys[css->ss->subsys_id] : NULL; +} + /** * cgroup_css - obtain a cgroup's css for the specified subsystem * @cgrp: the cgroup of interest diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 1db686e47a22..657a73cd44c4 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -62,11 +62,7 @@ static inline struct freezer *task_freezer(struct task_struct *task) static struct freezer *parent_freezer(struct freezer *freezer) { - struct cgroup *pcg = freezer->css.cgroup->parent; - - if (pcg) - return cgroup_freezer(pcg); - return NULL; + return css_freezer(css_parent(&freezer->css)); } bool cgroup_freezing(struct task_struct *task) @@ -234,7 +230,7 @@ static void freezer_fork(struct task_struct *task) * The root cgroup is non-freezable, so we can skip the * following check. */ - if (!freezer->css.cgroup->parent) + if (!parent_freezer(freezer)) goto out; spin_lock_irq(&freezer->lock); diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 6e9cbdde25bd..259a4af37e69 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -133,11 +133,7 @@ static inline struct cpuset *task_cs(struct task_struct *task) static inline struct cpuset *parent_cs(struct cpuset *cs) { - struct cgroup *pcgrp = cs->css.cgroup->parent; - - if (pcgrp) - return cgroup_cs(pcgrp); - return NULL; + return css_cs(css_parent(&cs->css)); } #ifdef CONFIG_NUMA diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 5bccb0277129..7a10742b389a 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7114,13 +7114,10 @@ static struct cgroup_subsys_state *cpu_cgroup_css_alloc(struct cgroup *cgrp) static int cpu_cgroup_css_online(struct cgroup *cgrp) { struct task_group *tg = cgroup_tg(cgrp); - struct task_group *parent; + struct task_group *parent = css_tg(css_parent(&tg->css)); - if (!cgrp->parent) - return 0; - - parent = cgroup_tg(cgrp->parent); - sched_online_group(tg, parent); + if (parent) + sched_online_group(tg, parent); return 0; } diff --git a/kernel/sched/cpuacct.c b/kernel/sched/cpuacct.c index 8ccfa10cc89f..f6926a149a71 100644 --- a/kernel/sched/cpuacct.c +++ b/kernel/sched/cpuacct.c @@ -50,16 +50,9 @@ static inline struct cpuacct *task_ca(struct task_struct *tsk) return css_ca(task_css(tsk, cpuacct_subsys_id)); } -static inline struct cpuacct *__parent_ca(struct cpuacct *ca) -{ - return cgroup_ca(ca->css.cgroup->parent); -} - static inline struct cpuacct *parent_ca(struct cpuacct *ca) { - if (!ca->css.cgroup->parent) - return NULL; - return cgroup_ca(ca->css.cgroup->parent); + return css_ca(css_parent(&ca->css)); } static DEFINE_PER_CPU(u64, root_cpuacct_cpuusage); @@ -284,7 +277,7 @@ void cpuacct_account_field(struct task_struct *p, int index, u64 val) while (ca != &root_cpuacct) { kcpustat = this_cpu_ptr(ca->cpustat); kcpustat->cpustat[index] += val; - ca = __parent_ca(ca); + ca = parent_ca(ca); } rcu_read_unlock(); } diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index 95585a0b9c8d..57ecb5d2513f 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -59,11 +59,7 @@ static inline bool hugetlb_cgroup_is_root(struct hugetlb_cgroup *h_cg) static inline struct hugetlb_cgroup * parent_hugetlb_cgroup(struct hugetlb_cgroup *h_cg) { - struct cgroup *parent = h_cg->css.cgroup->parent; - - if (!parent) - return NULL; - return hugetlb_cgroup_from_cgroup(parent); + return hugetlb_cgroup_from_css(css_parent(&h_cg->css)); } static inline bool hugetlb_cgroup_have_usage(struct hugetlb_cgroup *h_cg) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 11d659e3b08e..69b3e520f921 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1524,10 +1524,8 @@ static unsigned long mem_cgroup_margin(struct mem_cgroup *memcg) int mem_cgroup_swappiness(struct mem_cgroup *memcg) { - struct cgroup *cgrp = memcg->css.cgroup; - /* root ? */ - if (cgrp->parent == NULL) + if (!css_parent(&memcg->css)) return vm_swappiness; return memcg->swappiness; @@ -5026,11 +5024,7 @@ static int mem_cgroup_hierarchy_write(struct cgroup *cont, struct cftype *cft, { int retval = 0; struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); - struct cgroup *parent = cont->parent; - struct mem_cgroup *parent_memcg = NULL; - - if (parent) - parent_memcg = mem_cgroup_from_cont(parent); + struct mem_cgroup *parent_memcg = mem_cgroup_from_css(css_parent(&memcg->css)); mutex_lock(&memcg_create_mutex); @@ -5282,18 +5276,15 @@ static int mem_cgroup_write(struct cgroup *cont, struct cftype *cft, static void memcg_get_hierarchical_limit(struct mem_cgroup *memcg, unsigned long long *mem_limit, unsigned long long *memsw_limit) { - struct cgroup *cgroup; unsigned long long min_limit, min_memsw_limit, tmp; min_limit = res_counter_read_u64(&memcg->res, RES_LIMIT); min_memsw_limit = res_counter_read_u64(&memcg->memsw, RES_LIMIT); - cgroup = memcg->css.cgroup; if (!memcg->use_hierarchy) goto out; - while (cgroup->parent) { - cgroup = cgroup->parent; - memcg = mem_cgroup_from_cont(cgroup); + while (css_parent(&memcg->css)) { + memcg = mem_cgroup_from_css(css_parent(&memcg->css)); if (!memcg->use_hierarchy) break; tmp = res_counter_read_u64(&memcg->res, RES_LIMIT); @@ -5523,16 +5514,11 @@ static int mem_cgroup_swappiness_write(struct cgroup *cgrp, struct cftype *cft, u64 val) { struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); - struct mem_cgroup *parent; - - if (val > 100) - return -EINVAL; + struct mem_cgroup *parent = mem_cgroup_from_css(css_parent(&memcg->css)); - if (cgrp->parent == NULL) + if (val > 100 || !parent) return -EINVAL; - parent = mem_cgroup_from_cont(cgrp->parent); - mutex_lock(&memcg_create_mutex); /* If under hierarchy, only empty-root can set this value */ @@ -5861,14 +5847,12 @@ static int mem_cgroup_oom_control_write(struct cgroup *cgrp, struct cftype *cft, u64 val) { struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); - struct mem_cgroup *parent; + struct mem_cgroup *parent = mem_cgroup_from_css(css_parent(&memcg->css)); /* cannot set to root cgroup and only 0 and 1 are allowed */ - if (!cgrp->parent || !((val == 0) || (val == 1))) + if (!parent || !((val == 0) || (val == 1))) return -EINVAL; - parent = mem_cgroup_from_cont(cgrp->parent); - mutex_lock(&memcg_create_mutex); /* oom-kill-disable is a flag for subhierarchy. */ if ((parent->use_hierarchy) || memcg_has_children(memcg)) { @@ -6266,15 +6250,14 @@ free_out: static int mem_cgroup_css_online(struct cgroup *cont) { - struct mem_cgroup *memcg, *parent; + struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *parent = mem_cgroup_from_css(css_parent(&memcg->css)); int error = 0; - if (!cont->parent) + if (!parent) return 0; mutex_lock(&memcg_create_mutex); - memcg = mem_cgroup_from_cont(cont); - parent = mem_cgroup_from_cont(cont->parent); memcg->use_hierarchy = parent->use_hierarchy; memcg->oom_kill_disable = parent->oom_kill_disable; diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index af412ab2b477..9e6b75e5efce 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -50,9 +50,11 @@ static struct cgroup_subsys_state *cgrp_css_alloc(struct cgroup *cgrp) static int cgrp_css_online(struct cgroup *cgrp) { - if (cgrp->parent) - cgrp_cls_state(cgrp)->classid = - cgrp_cls_state(cgrp->parent)->classid; + struct cgroup_cls_state *cs = cgrp_cls_state(cgrp); + struct cgroup_cls_state *parent = css_cls_state(css_parent(&cs->css)); + + if (parent) + cs->classid = parent->classid; return 0; } diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 90953648c643..635a49db005d 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -198,13 +198,11 @@ static inline bool is_devcg_online(const struct dev_cgroup *devcg) */ static int devcgroup_online(struct cgroup *cgroup) { - struct dev_cgroup *dev_cgroup, *parent_dev_cgroup = NULL; + struct dev_cgroup *dev_cgroup = cgroup_to_devcgroup(cgroup); + struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css_parent(&dev_cgroup->css)); int ret = 0; mutex_lock(&devcgroup_mutex); - dev_cgroup = cgroup_to_devcgroup(cgroup); - if (cgroup->parent) - parent_dev_cgroup = cgroup_to_devcgroup(cgroup->parent); if (parent_dev_cgroup == NULL) dev_cgroup->behavior = DEVCG_DEFAULT_ALLOW; @@ -394,12 +392,10 @@ static bool may_access(struct dev_cgroup *dev_cgroup, static int parent_has_perm(struct dev_cgroup *childcg, struct dev_exception_item *ex) { - struct cgroup *pcg = childcg->css.cgroup->parent; - struct dev_cgroup *parent; + struct dev_cgroup *parent = css_to_devcgroup(css_parent(&childcg->css)); - if (!pcg) + if (!parent) return 1; - parent = cgroup_to_devcgroup(pcg); return may_access(parent, ex, childcg->behavior); } @@ -524,15 +520,11 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, char temp[12]; /* 11 + 1 characters needed for a u32 */ int count, rc = 0; struct dev_exception_item ex; - struct cgroup *p = devcgroup->css.cgroup; - struct dev_cgroup *parent = NULL; + struct dev_cgroup *parent = css_to_devcgroup(css_parent(&devcgroup->css)); if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (p->parent) - parent = cgroup_to_devcgroup(p->parent); - memset(&ex, 0, sizeof(ex)); b = buffer; -- cgit v1.2.3-71-gd317 From eb95419b023abacb415e2a18fea899023ce7624d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:23 -0400 Subject: cgroup: pass around cgroup_subsys_state instead of cgroup in subsystem methods cgroup is currently in the process of transitioning to using struct cgroup_subsys_state * as the primary handle instead of struct cgroup * in subsystem implementations for the following reasons. * With unified hierarchy, subsystems will be dynamically bound and unbound from cgroups and thus css's (cgroup_subsys_state) may be created and destroyed dynamically over the lifetime of a cgroup, which is different from the current state where all css's are allocated and destroyed together with the associated cgroup. This in turn means that cgroup_css() should be synchronized and may return NULL, making it more cumbersome to use. * Differing levels of per-subsystem granularity in the unified hierarchy means that the task and descendant iterators should behave differently depending on the specific subsystem the iteration is being performed for. * In majority of the cases, subsystems only care about its part in the cgroup hierarchy - ie. the hierarchy of css's. Subsystem methods often obtain the matching css pointer from the cgroup and don't bother with the cgroup pointer itself. Passing around css fits much better. This patch converts all cgroup_subsys methods to take @css instead of @cgroup. The conversions are mostly straight-forward. A few noteworthy changes are * ->css_alloc() now takes css of the parent cgroup rather than the pointer to the new cgroup as the css for the new cgroup doesn't exist yet. Knowing the parent css is enough for all the existing subsystems. * In kernel/cgroup.c::offline_css(), unnecessary open coded css dereference is replaced with local variable access. This patch shouldn't cause any behavior differences. v2: Unnecessary explicit cgrp->subsys[] deref in css_online() replaced with local variable @css as suggested by Li Zefan. Rebased on top of new for-3.12 which includes for-3.11-fixes so that ->css_free() invocation added by da0a12caff ("cgroup: fix a leak when percpu_ref_init() fails") is converted too. Suggested by Li Zefan. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Michal Hocko Acked-by: Vivek Goyal Acked-by: Aristeu Rozanski Acked-by: Daniel Wagner Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Johannes Weiner Cc: Balbir Singh Cc: Matt Helsley Cc: Jens Axboe Cc: Steven Rostedt --- block/blk-cgroup.c | 25 +++++++++++---------- include/linux/cgroup.h | 24 +++++++++++--------- kernel/cgroup.c | 57 ++++++++++++++++++++++++++++------------------- kernel/cgroup_freezer.c | 40 +++++++++++++++++---------------- kernel/cpuset.c | 39 +++++++++++++++++--------------- kernel/events/core.c | 18 ++++++++------- kernel/sched/core.c | 39 ++++++++++++++++---------------- kernel/sched/cpuacct.c | 9 ++++---- mm/hugetlb_cgroup.c | 19 ++++++++-------- mm/memcontrol.c | 38 +++++++++++++++---------------- net/core/netprio_cgroup.c | 20 ++++++++--------- net/sched/cls_cgroup.c | 18 ++++++++------- security/device_cgroup.c | 22 +++++++++--------- 13 files changed, 197 insertions(+), 171 deletions(-) (limited to 'include') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 290792a13e3c..79fd9f4fadb7 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -765,18 +765,18 @@ struct cftype blkcg_files[] = { /** * blkcg_css_offline - cgroup css_offline callback - * @cgroup: cgroup of interest + * @css: css of interest * - * This function is called when @cgroup is about to go away and responsible - * for shooting down all blkgs associated with @cgroup. blkgs should be + * This function is called when @css is about to go away and responsible + * for shooting down all blkgs associated with @css. blkgs should be * removed while holding both q and blkcg locks. As blkcg lock is nested * inside q lock, this function performs reverse double lock dancing. * * This is the blkcg counterpart of ioc_release_fn(). */ -static void blkcg_css_offline(struct cgroup *cgroup) +static void blkcg_css_offline(struct cgroup_subsys_state *css) { - struct blkcg *blkcg = cgroup_to_blkcg(cgroup); + struct blkcg *blkcg = css_to_blkcg(css); spin_lock_irq(&blkcg->lock); @@ -798,21 +798,21 @@ static void blkcg_css_offline(struct cgroup *cgroup) spin_unlock_irq(&blkcg->lock); } -static void blkcg_css_free(struct cgroup *cgroup) +static void blkcg_css_free(struct cgroup_subsys_state *css) { - struct blkcg *blkcg = cgroup_to_blkcg(cgroup); + struct blkcg *blkcg = css_to_blkcg(css); if (blkcg != &blkcg_root) kfree(blkcg); } -static struct cgroup_subsys_state *blkcg_css_alloc(struct cgroup *cgroup) +static struct cgroup_subsys_state * +blkcg_css_alloc(struct cgroup_subsys_state *parent_css) { static atomic64_t id_seq = ATOMIC64_INIT(0); struct blkcg *blkcg; - struct cgroup *parent = cgroup->parent; - if (!parent) { + if (!parent_css) { blkcg = &blkcg_root; goto done; } @@ -883,14 +883,15 @@ void blkcg_exit_queue(struct request_queue *q) * of the main cic data structures. For now we allow a task to change * its cgroup only if it's the only owner of its ioc. */ -static int blkcg_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) +static int blkcg_can_attach(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset) { struct task_struct *task; struct io_context *ioc; int ret = 0; /* task_lock() is needed to avoid races with exit_io_context() */ - cgroup_taskset_for_each(task, cgrp, tset) { + cgroup_taskset_for_each(task, css->cgroup, tset) { task_lock(task); ioc = task->io_context; if (ioc && atomic_read(&ioc->nr_tasks) > 1) diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 18112a3bb12b..9c2b9dd9121d 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -579,18 +579,22 @@ int cgroup_taskset_size(struct cgroup_taskset *tset); */ struct cgroup_subsys { - struct cgroup_subsys_state *(*css_alloc)(struct cgroup *cgrp); - int (*css_online)(struct cgroup *cgrp); - void (*css_offline)(struct cgroup *cgrp); - void (*css_free)(struct cgroup *cgrp); - - int (*can_attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); - void (*cancel_attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); - void (*attach)(struct cgroup *cgrp, struct cgroup_taskset *tset); + struct cgroup_subsys_state *(*css_alloc)(struct cgroup_subsys_state *parent_css); + int (*css_online)(struct cgroup_subsys_state *css); + void (*css_offline)(struct cgroup_subsys_state *css); + void (*css_free)(struct cgroup_subsys_state *css); + + int (*can_attach)(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset); + void (*cancel_attach)(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset); + void (*attach)(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset); void (*fork)(struct task_struct *task); - void (*exit)(struct cgroup *cgrp, struct cgroup *old_cgrp, + void (*exit)(struct cgroup_subsys_state *css, + struct cgroup_subsys_state *old_css, struct task_struct *task); - void (*bind)(struct cgroup *root); + void (*bind)(struct cgroup_subsys_state *root_css); int subsys_id; int disabled; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 4234428f1014..271d9a5cde5f 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -853,8 +853,11 @@ static void cgroup_free_fn(struct work_struct *work) /* * Release the subsystem state objects. */ - for_each_root_subsys(cgrp->root, ss) - ss->css_free(cgrp); + for_each_root_subsys(cgrp->root, ss) { + struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id]; + + ss->css_free(css); + } cgrp->root->number_of_cgroups--; mutex_unlock(&cgroup_mutex); @@ -1056,7 +1059,7 @@ static int rebind_subsystems(struct cgroupfs_root *root, list_move(&ss->sibling, &root->subsys_list); ss->root = root; if (ss->bind) - ss->bind(cgrp); + ss->bind(cgrp->subsys[i]); /* refcount was already taken, and we're keeping it */ root->subsys_mask |= bit; @@ -1066,7 +1069,7 @@ static int rebind_subsystems(struct cgroupfs_root *root, BUG_ON(cgrp->subsys[i]->cgroup != cgrp); if (ss->bind) - ss->bind(cgroup_dummy_top); + ss->bind(cgroup_dummy_top->subsys[i]); cgroup_dummy_top->subsys[i]->cgroup = cgroup_dummy_top; cgrp->subsys[i] = NULL; cgroup_subsys[i]->root = &cgroup_dummy_root; @@ -2049,8 +2052,10 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk, * step 1: check that we can legitimately attach to the cgroup. */ for_each_root_subsys(root, ss) { + struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id]; + if (ss->can_attach) { - retval = ss->can_attach(cgrp, &tset); + retval = ss->can_attach(css, &tset); if (retval) { failed_ss = ss; goto out_cancel_attach; @@ -2089,8 +2094,10 @@ static int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk, * step 4: do subsystem attach callbacks. */ for_each_root_subsys(root, ss) { + struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id]; + if (ss->attach) - ss->attach(cgrp, &tset); + ss->attach(css, &tset); } /* @@ -2109,10 +2116,12 @@ out_put_css_set_refs: out_cancel_attach: if (retval) { for_each_root_subsys(root, ss) { + struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id]; + if (ss == failed_ss) break; if (ss->cancel_attach) - ss->cancel_attach(cgrp, &tset); + ss->cancel_attach(css, &tset); } } out_free_group_list: @@ -4206,14 +4215,15 @@ static void init_cgroup_css(struct cgroup_subsys_state *css, /* invoke ->css_online() on a new CSS and mark it online if successful */ static int online_css(struct cgroup_subsys *ss, struct cgroup *cgrp) { + struct cgroup_subsys_state *css = cgrp->subsys[ss->subsys_id]; int ret = 0; lockdep_assert_held(&cgroup_mutex); if (ss->css_online) - ret = ss->css_online(cgrp); + ret = ss->css_online(css); if (!ret) - cgrp->subsys[ss->subsys_id]->flags |= CSS_ONLINE; + css->flags |= CSS_ONLINE; return ret; } @@ -4228,9 +4238,9 @@ static void offline_css(struct cgroup_subsys *ss, struct cgroup *cgrp) return; if (ss->css_offline) - ss->css_offline(cgrp); + ss->css_offline(css); - cgrp->subsys[ss->subsys_id]->flags &= ~CSS_ONLINE; + css->flags &= ~CSS_ONLINE; } /* @@ -4305,7 +4315,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, for_each_root_subsys(root, ss) { struct cgroup_subsys_state *css; - css = ss->css_alloc(cgrp); + css = ss->css_alloc(parent->subsys[ss->subsys_id]); if (IS_ERR(css)) { err = PTR_ERR(css); goto err_free_all; @@ -4313,7 +4323,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, err = percpu_ref_init(&css->refcnt, css_release); if (err) { - ss->css_free(cgrp); + ss->css_free(css); goto err_free_all; } @@ -4386,7 +4396,7 @@ err_free_all: if (css) { percpu_ref_cancel_init(&css->refcnt); - ss->css_free(cgrp); + ss->css_free(css); } } mutex_unlock(&cgroup_mutex); @@ -4641,7 +4651,7 @@ static void __init cgroup_init_subsys(struct cgroup_subsys *ss) /* Create the top cgroup state for this subsystem */ list_add(&ss->sibling, &cgroup_dummy_root.subsys_list); ss->root = &cgroup_dummy_root; - css = ss->css_alloc(cgroup_dummy_top); + css = ss->css_alloc(cgroup_dummy_top->subsys[ss->subsys_id]); /* We don't handle early failures gracefully */ BUG_ON(IS_ERR(css)); init_cgroup_css(css, ss, cgroup_dummy_top); @@ -4720,7 +4730,7 @@ int __init_or_module cgroup_load_subsys(struct cgroup_subsys *ss) * struct, so this can happen first (i.e. before the dummy root * attachment). */ - css = ss->css_alloc(cgroup_dummy_top); + css = ss->css_alloc(cgroup_dummy_top->subsys[ss->subsys_id]); if (IS_ERR(css)) { /* failure case - need to deassign the cgroup_subsys[] slot. */ cgroup_subsys[ss->subsys_id] = NULL; @@ -4836,7 +4846,7 @@ void cgroup_unload_subsys(struct cgroup_subsys *ss) * the cgrp->subsys pointer to find their state. note that this * also takes care of freeing the css_id. */ - ss->css_free(cgroup_dummy_top); + ss->css_free(cgroup_dummy_top->subsys[ss->subsys_id]); cgroup_dummy_top->subsys[ss->subsys_id] = NULL; mutex_unlock(&cgroup_mutex); @@ -5192,10 +5202,10 @@ void cgroup_exit(struct task_struct *tsk, int run_callbacks) */ for_each_builtin_subsys(ss, i) { if (ss->exit) { - struct cgroup *old_cgrp = cset->subsys[i]->cgroup; - struct cgroup *cgrp = task_cgroup(tsk, i); + struct cgroup_subsys_state *old_css = cset->subsys[i]; + struct cgroup_subsys_state *css = task_css(tsk, i); - ss->exit(cgrp, old_cgrp, tsk); + ss->exit(css, old_css, tsk); } } } @@ -5529,7 +5539,8 @@ struct cgroup_subsys_state *cgroup_css_from_dir(struct file *f, int id) } #ifdef CONFIG_CGROUP_DEBUG -static struct cgroup_subsys_state *debug_css_alloc(struct cgroup *cgrp) +static struct cgroup_subsys_state * +debug_css_alloc(struct cgroup_subsys_state *parent_css) { struct cgroup_subsys_state *css = kzalloc(sizeof(*css), GFP_KERNEL); @@ -5539,9 +5550,9 @@ static struct cgroup_subsys_state *debug_css_alloc(struct cgroup *cgrp) return css; } -static void debug_css_free(struct cgroup *cgrp) +static void debug_css_free(struct cgroup_subsys_state *css) { - kfree(cgrp->subsys[debug_subsys_id]); + kfree(css); } static u64 debug_taskcount_read(struct cgroup *cgrp, struct cftype *cft) diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 657a73cd44c4..f03a85719c3c 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -91,7 +91,8 @@ static const char *freezer_state_strs(unsigned int state) struct cgroup_subsys freezer_subsys; -static struct cgroup_subsys_state *freezer_css_alloc(struct cgroup *cgroup) +static struct cgroup_subsys_state * +freezer_css_alloc(struct cgroup_subsys_state *parent_css) { struct freezer *freezer; @@ -104,16 +105,16 @@ static struct cgroup_subsys_state *freezer_css_alloc(struct cgroup *cgroup) } /** - * freezer_css_online - commit creation of a freezer cgroup - * @cgroup: cgroup being created + * freezer_css_online - commit creation of a freezer css + * @css: css being created * - * We're committing to creation of @cgroup. Mark it online and inherit + * We're committing to creation of @css. Mark it online and inherit * parent's freezing state while holding both parent's and our * freezer->lock. */ -static int freezer_css_online(struct cgroup *cgroup) +static int freezer_css_online(struct cgroup_subsys_state *css) { - struct freezer *freezer = cgroup_freezer(cgroup); + struct freezer *freezer = css_freezer(css); struct freezer *parent = parent_freezer(freezer); /* @@ -140,15 +141,15 @@ static int freezer_css_online(struct cgroup *cgroup) } /** - * freezer_css_offline - initiate destruction of @cgroup - * @cgroup: cgroup being destroyed + * freezer_css_offline - initiate destruction of a freezer css + * @css: css being destroyed * - * @cgroup is going away. Mark it dead and decrement system_freezing_count - * if it was holding one. + * @css is going away. Mark it dead and decrement system_freezing_count if + * it was holding one. */ -static void freezer_css_offline(struct cgroup *cgroup) +static void freezer_css_offline(struct cgroup_subsys_state *css) { - struct freezer *freezer = cgroup_freezer(cgroup); + struct freezer *freezer = css_freezer(css); spin_lock_irq(&freezer->lock); @@ -160,9 +161,9 @@ static void freezer_css_offline(struct cgroup *cgroup) spin_unlock_irq(&freezer->lock); } -static void freezer_css_free(struct cgroup *cgroup) +static void freezer_css_free(struct cgroup_subsys_state *css) { - kfree(cgroup_freezer(cgroup)); + kfree(css_freezer(css)); } /* @@ -174,25 +175,26 @@ static void freezer_css_free(struct cgroup *cgroup) * @freezer->lock. freezer_attach() makes the new tasks conform to the * current state and all following state changes can see the new tasks. */ -static void freezer_attach(struct cgroup *new_cgrp, struct cgroup_taskset *tset) +static void freezer_attach(struct cgroup_subsys_state *new_css, + struct cgroup_taskset *tset) { - struct freezer *freezer = cgroup_freezer(new_cgrp); + struct freezer *freezer = css_freezer(new_css); struct task_struct *task; bool clear_frozen = false; spin_lock_irq(&freezer->lock); /* - * Make the new tasks conform to the current state of @new_cgrp. + * Make the new tasks conform to the current state of @new_css. * For simplicity, when migrating any task to a FROZEN cgroup, we * revert it to FREEZING and let update_if_frozen() determine the * correct state later. * - * Tasks in @tset are on @new_cgrp but may not conform to its + * Tasks in @tset are on @new_css but may not conform to its * current state before executing the following - !frozen tasks may * be visible in a FROZEN cgroup and frozen tasks in a THAWED one. */ - cgroup_taskset_for_each(task, new_cgrp, tset) { + cgroup_taskset_for_each(task, new_css->cgroup, tset) { if (!(freezer->state & CGROUP_FREEZING)) { __thaw_task(task); } else { diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 259a4af37e69..8ce3fdc3dfcc 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -1455,9 +1455,10 @@ static int fmeter_getrate(struct fmeter *fmp) } /* Called by cgroups to determine if a cpuset is usable; cpuset_mutex held */ -static int cpuset_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) +static int cpuset_can_attach(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset) { - struct cpuset *cs = cgroup_cs(cgrp); + struct cpuset *cs = css_cs(css); struct task_struct *task; int ret; @@ -1468,11 +1469,11 @@ static int cpuset_can_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) * flag is set. */ ret = -ENOSPC; - if (!cgroup_sane_behavior(cgrp) && + if (!cgroup_sane_behavior(css->cgroup) && (cpumask_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed))) goto out_unlock; - cgroup_taskset_for_each(task, cgrp, tset) { + cgroup_taskset_for_each(task, css->cgroup, tset) { /* * Kthreads which disallow setaffinity shouldn't be moved * to a new cpuset; we don't want to change their cpu @@ -1501,11 +1502,11 @@ out_unlock: return ret; } -static void cpuset_cancel_attach(struct cgroup *cgrp, +static void cpuset_cancel_attach(struct cgroup_subsys_state *css, struct cgroup_taskset *tset) { mutex_lock(&cpuset_mutex); - cgroup_cs(cgrp)->attach_in_progress--; + css_cs(css)->attach_in_progress--; mutex_unlock(&cpuset_mutex); } @@ -1516,7 +1517,8 @@ static void cpuset_cancel_attach(struct cgroup *cgrp, */ static cpumask_var_t cpus_attach; -static void cpuset_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) +static void cpuset_attach(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset) { /* static buf protected by cpuset_mutex */ static nodemask_t cpuset_attach_nodemask_to; @@ -1524,7 +1526,7 @@ static void cpuset_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) struct task_struct *task; struct task_struct *leader = cgroup_taskset_first(tset); struct cgroup *oldcgrp = cgroup_taskset_cur_cgroup(tset); - struct cpuset *cs = cgroup_cs(cgrp); + struct cpuset *cs = css_cs(css); struct cpuset *oldcs = cgroup_cs(oldcgrp); struct cpuset *cpus_cs = effective_cpumask_cpuset(cs); struct cpuset *mems_cs = effective_nodemask_cpuset(cs); @@ -1539,7 +1541,7 @@ static void cpuset_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) guarantee_online_mems(mems_cs, &cpuset_attach_nodemask_to); - cgroup_taskset_for_each(task, cgrp, tset) { + cgroup_taskset_for_each(task, css->cgroup, tset) { /* * can_attach beforehand should guarantee that this doesn't * fail. TODO: have a better way to handle failure here @@ -1940,11 +1942,12 @@ static struct cftype files[] = { * cgrp: control group that the new cpuset will be part of */ -static struct cgroup_subsys_state *cpuset_css_alloc(struct cgroup *cgrp) +static struct cgroup_subsys_state * +cpuset_css_alloc(struct cgroup_subsys_state *parent_css) { struct cpuset *cs; - if (!cgrp->parent) + if (!parent_css) return &top_cpuset.css; cs = kzalloc(sizeof(*cs), GFP_KERNEL); @@ -1964,9 +1967,9 @@ static struct cgroup_subsys_state *cpuset_css_alloc(struct cgroup *cgrp) return &cs->css; } -static int cpuset_css_online(struct cgroup *cgrp) +static int cpuset_css_online(struct cgroup_subsys_state *css) { - struct cpuset *cs = cgroup_cs(cgrp); + struct cpuset *cs = css_cs(css); struct cpuset *parent = parent_cs(cs); struct cpuset *tmp_cs; struct cgroup *pos_cgrp; @@ -1984,7 +1987,7 @@ static int cpuset_css_online(struct cgroup *cgrp) number_of_cpusets++; - if (!test_bit(CGRP_CPUSET_CLONE_CHILDREN, &cgrp->flags)) + if (!test_bit(CGRP_CPUSET_CLONE_CHILDREN, &css->cgroup->flags)) goto out_unlock; /* @@ -2024,9 +2027,9 @@ out_unlock: * will call rebuild_sched_domains_locked(). */ -static void cpuset_css_offline(struct cgroup *cgrp) +static void cpuset_css_offline(struct cgroup_subsys_state *css) { - struct cpuset *cs = cgroup_cs(cgrp); + struct cpuset *cs = css_cs(css); mutex_lock(&cpuset_mutex); @@ -2039,9 +2042,9 @@ static void cpuset_css_offline(struct cgroup *cgrp) mutex_unlock(&cpuset_mutex); } -static void cpuset_css_free(struct cgroup *cgrp) +static void cpuset_css_free(struct cgroup_subsys_state *css) { - struct cpuset *cs = cgroup_cs(cgrp); + struct cpuset *cs = css_cs(css); free_cpumask_var(cs->cpus_allowed); kfree(cs); diff --git a/kernel/events/core.c b/kernel/events/core.c index 414c61f4d776..9705a0ed1dce 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7778,7 +7778,8 @@ unlock: device_initcall(perf_event_sysfs_init); #ifdef CONFIG_CGROUP_PERF -static struct cgroup_subsys_state *perf_cgroup_css_alloc(struct cgroup *cont) +static struct cgroup_subsys_state * +perf_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) { struct perf_cgroup *jc; @@ -7795,11 +7796,10 @@ static struct cgroup_subsys_state *perf_cgroup_css_alloc(struct cgroup *cont) return &jc->css; } -static void perf_cgroup_css_free(struct cgroup *cont) +static void perf_cgroup_css_free(struct cgroup_subsys_state *css) { - struct perf_cgroup *jc; - jc = container_of(cgroup_css(cont, perf_subsys_id), - struct perf_cgroup, css); + struct perf_cgroup *jc = container_of(css, struct perf_cgroup, css); + free_percpu(jc->info); kfree(jc); } @@ -7811,15 +7811,17 @@ static int __perf_cgroup_move(void *info) return 0; } -static void perf_cgroup_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) +static void perf_cgroup_attach(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset) { struct task_struct *task; - cgroup_taskset_for_each(task, cgrp, tset) + cgroup_taskset_for_each(task, css->cgroup, tset) task_function_call(task, __perf_cgroup_move, task); } -static void perf_cgroup_exit(struct cgroup *cgrp, struct cgroup *old_cgrp, +static void perf_cgroup_exit(struct cgroup_subsys_state *css, + struct cgroup_subsys_state *old_css, struct task_struct *task) { /* diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 7a10742b389a..622b7efc5ade 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7094,16 +7094,17 @@ static inline struct task_group *cgroup_tg(struct cgroup *cgrp) return css_tg(cgroup_css(cgrp, cpu_cgroup_subsys_id)); } -static struct cgroup_subsys_state *cpu_cgroup_css_alloc(struct cgroup *cgrp) +static struct cgroup_subsys_state * +cpu_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) { - struct task_group *tg, *parent; + struct task_group *parent = css_tg(parent_css); + struct task_group *tg; - if (!cgrp->parent) { + if (!parent) { /* This is early initialization for the top cgroup */ return &root_task_group.css; } - parent = cgroup_tg(cgrp->parent); tg = sched_create_group(parent); if (IS_ERR(tg)) return ERR_PTR(-ENOMEM); @@ -7111,38 +7112,38 @@ static struct cgroup_subsys_state *cpu_cgroup_css_alloc(struct cgroup *cgrp) return &tg->css; } -static int cpu_cgroup_css_online(struct cgroup *cgrp) +static int cpu_cgroup_css_online(struct cgroup_subsys_state *css) { - struct task_group *tg = cgroup_tg(cgrp); - struct task_group *parent = css_tg(css_parent(&tg->css)); + struct task_group *tg = css_tg(css); + struct task_group *parent = css_tg(css_parent(css)); if (parent) sched_online_group(tg, parent); return 0; } -static void cpu_cgroup_css_free(struct cgroup *cgrp) +static void cpu_cgroup_css_free(struct cgroup_subsys_state *css) { - struct task_group *tg = cgroup_tg(cgrp); + struct task_group *tg = css_tg(css); sched_destroy_group(tg); } -static void cpu_cgroup_css_offline(struct cgroup *cgrp) +static void cpu_cgroup_css_offline(struct cgroup_subsys_state *css) { - struct task_group *tg = cgroup_tg(cgrp); + struct task_group *tg = css_tg(css); sched_offline_group(tg); } -static int cpu_cgroup_can_attach(struct cgroup *cgrp, +static int cpu_cgroup_can_attach(struct cgroup_subsys_state *css, struct cgroup_taskset *tset) { struct task_struct *task; - cgroup_taskset_for_each(task, cgrp, tset) { + cgroup_taskset_for_each(task, css->cgroup, tset) { #ifdef CONFIG_RT_GROUP_SCHED - if (!sched_rt_can_attach(cgroup_tg(cgrp), task)) + if (!sched_rt_can_attach(css_tg(css), task)) return -EINVAL; #else /* We don't support RT-tasks being in separate groups */ @@ -7153,18 +7154,18 @@ static int cpu_cgroup_can_attach(struct cgroup *cgrp, return 0; } -static void cpu_cgroup_attach(struct cgroup *cgrp, +static void cpu_cgroup_attach(struct cgroup_subsys_state *css, struct cgroup_taskset *tset) { struct task_struct *task; - cgroup_taskset_for_each(task, cgrp, tset) + cgroup_taskset_for_each(task, css->cgroup, tset) sched_move_task(task); } -static void -cpu_cgroup_exit(struct cgroup *cgrp, struct cgroup *old_cgrp, - struct task_struct *task) +static void cpu_cgroup_exit(struct cgroup_subsys_state *css, + struct cgroup_subsys_state *old_css, + struct task_struct *task) { /* * cgroup_exit() is called in the copy_process() failure path. diff --git a/kernel/sched/cpuacct.c b/kernel/sched/cpuacct.c index f6926a149a71..1b784d9b3630 100644 --- a/kernel/sched/cpuacct.c +++ b/kernel/sched/cpuacct.c @@ -62,11 +62,12 @@ static struct cpuacct root_cpuacct = { }; /* create a new cpu accounting group */ -static struct cgroup_subsys_state *cpuacct_css_alloc(struct cgroup *cgrp) +static struct cgroup_subsys_state * +cpuacct_css_alloc(struct cgroup_subsys_state *parent_css) { struct cpuacct *ca; - if (!cgrp->parent) + if (!parent_css) return &root_cpuacct.css; ca = kzalloc(sizeof(*ca), GFP_KERNEL); @@ -92,9 +93,9 @@ out: } /* destroy an existing cpu accounting group */ -static void cpuacct_css_free(struct cgroup *cgrp) +static void cpuacct_css_free(struct cgroup_subsys_state *css) { - struct cpuacct *ca = cgroup_ca(cgrp); + struct cpuacct *ca = css_ca(css); free_percpu(ca->cpustat); free_percpu(ca->cpuusage); diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index 57ecb5d2513f..e2132435060f 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -73,19 +73,18 @@ static inline bool hugetlb_cgroup_have_usage(struct hugetlb_cgroup *h_cg) return false; } -static struct cgroup_subsys_state *hugetlb_cgroup_css_alloc(struct cgroup *cgroup) +static struct cgroup_subsys_state * +hugetlb_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) { + struct hugetlb_cgroup *parent_h_cgroup = hugetlb_cgroup_from_css(parent_css); + struct hugetlb_cgroup *h_cgroup; int idx; - struct cgroup *parent_cgroup; - struct hugetlb_cgroup *h_cgroup, *parent_h_cgroup; h_cgroup = kzalloc(sizeof(*h_cgroup), GFP_KERNEL); if (!h_cgroup) return ERR_PTR(-ENOMEM); - parent_cgroup = cgroup->parent; - if (parent_cgroup) { - parent_h_cgroup = hugetlb_cgroup_from_cgroup(parent_cgroup); + if (parent_h_cgroup) { for (idx = 0; idx < HUGE_MAX_HSTATE; idx++) res_counter_init(&h_cgroup->hugepage[idx], &parent_h_cgroup->hugepage[idx]); @@ -97,11 +96,11 @@ static struct cgroup_subsys_state *hugetlb_cgroup_css_alloc(struct cgroup *cgrou return &h_cgroup->css; } -static void hugetlb_cgroup_css_free(struct cgroup *cgroup) +static void hugetlb_cgroup_css_free(struct cgroup_subsys_state *css) { struct hugetlb_cgroup *h_cgroup; - h_cgroup = hugetlb_cgroup_from_cgroup(cgroup); + h_cgroup = hugetlb_cgroup_from_css(css); kfree(h_cgroup); } @@ -150,9 +149,9 @@ out: * Force the hugetlb cgroup to empty the hugetlb resources by moving them to * the parent cgroup. */ -static void hugetlb_cgroup_css_offline(struct cgroup *cgroup) +static void hugetlb_cgroup_css_offline(struct cgroup_subsys_state *css) { - struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup); + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(css); struct hstate *h; struct page *page; int idx = 0; diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 69b3e520f921..32cca0f0af0d 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -6211,7 +6211,7 @@ static void __init mem_cgroup_soft_limit_tree_init(void) } static struct cgroup_subsys_state * __ref -mem_cgroup_css_alloc(struct cgroup *cont) +mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) { struct mem_cgroup *memcg; long error = -ENOMEM; @@ -6226,7 +6226,7 @@ mem_cgroup_css_alloc(struct cgroup *cont) goto free_out; /* root ? */ - if (cont->parent == NULL) { + if (parent_css == NULL) { root_mem_cgroup = memcg; res_counter_init(&memcg->res, NULL); res_counter_init(&memcg->memsw, NULL); @@ -6248,10 +6248,10 @@ free_out: } static int -mem_cgroup_css_online(struct cgroup *cont) +mem_cgroup_css_online(struct cgroup_subsys_state *css) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); - struct mem_cgroup *parent = mem_cgroup_from_css(css_parent(&memcg->css)); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); + struct mem_cgroup *parent = mem_cgroup_from_css(css_parent(css)); int error = 0; if (!parent) @@ -6308,9 +6308,9 @@ static void mem_cgroup_invalidate_reclaim_iterators(struct mem_cgroup *memcg) mem_cgroup_iter_invalidate(root_mem_cgroup); } -static void mem_cgroup_css_offline(struct cgroup *cont) +static void mem_cgroup_css_offline(struct cgroup_subsys_state *css) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); kmem_cgroup_css_offline(memcg); @@ -6319,9 +6319,9 @@ static void mem_cgroup_css_offline(struct cgroup *cont) mem_cgroup_destroy_all_caches(memcg); } -static void mem_cgroup_css_free(struct cgroup *cont) +static void mem_cgroup_css_free(struct cgroup_subsys_state *css) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); memcg_destroy_kmem(memcg); __mem_cgroup_free(memcg); @@ -6691,12 +6691,12 @@ static void mem_cgroup_clear_mc(void) mem_cgroup_end_move(from); } -static int mem_cgroup_can_attach(struct cgroup *cgroup, +static int mem_cgroup_can_attach(struct cgroup_subsys_state *css, struct cgroup_taskset *tset) { struct task_struct *p = cgroup_taskset_first(tset); int ret = 0; - struct mem_cgroup *memcg = mem_cgroup_from_cont(cgroup); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); unsigned long move_charge_at_immigrate; /* @@ -6738,7 +6738,7 @@ static int mem_cgroup_can_attach(struct cgroup *cgroup, return ret; } -static void mem_cgroup_cancel_attach(struct cgroup *cgroup, +static void mem_cgroup_cancel_attach(struct cgroup_subsys_state *css, struct cgroup_taskset *tset) { mem_cgroup_clear_mc(); @@ -6886,7 +6886,7 @@ retry: up_read(&mm->mmap_sem); } -static void mem_cgroup_move_task(struct cgroup *cont, +static void mem_cgroup_move_task(struct cgroup_subsys_state *css, struct cgroup_taskset *tset) { struct task_struct *p = cgroup_taskset_first(tset); @@ -6901,16 +6901,16 @@ static void mem_cgroup_move_task(struct cgroup *cont, mem_cgroup_clear_mc(); } #else /* !CONFIG_MMU */ -static int mem_cgroup_can_attach(struct cgroup *cgroup, +static int mem_cgroup_can_attach(struct cgroup_subsys_state *css, struct cgroup_taskset *tset) { return 0; } -static void mem_cgroup_cancel_attach(struct cgroup *cgroup, +static void mem_cgroup_cancel_attach(struct cgroup_subsys_state *css, struct cgroup_taskset *tset) { } -static void mem_cgroup_move_task(struct cgroup *cont, +static void mem_cgroup_move_task(struct cgroup_subsys_state *css, struct cgroup_taskset *tset) { } @@ -6920,15 +6920,15 @@ static void mem_cgroup_move_task(struct cgroup *cont, * Cgroup retains root cgroups across [un]mount cycles making it necessary * to verify sane_behavior flag on each mount attempt. */ -static void mem_cgroup_bind(struct cgroup *root) +static void mem_cgroup_bind(struct cgroup_subsys_state *root_css) { /* * use_hierarchy is forced with sane_behavior. cgroup core * guarantees that @root doesn't have any children, so turning it * on for the root memcg is enough. */ - if (cgroup_sane_behavior(root)) - mem_cgroup_from_cont(root)->use_hierarchy = true; + if (cgroup_sane_behavior(root_css->cgroup)) + mem_cgroup_from_css(root_css)->use_hierarchy = true; } struct cgroup_subsys mem_cgroup_subsys = { diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index 5dfac8886e12..8d095b4c2f6f 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -126,7 +126,8 @@ static int netprio_set_prio(struct cgroup_subsys_state *css, return 0; } -static struct cgroup_subsys_state *cgrp_css_alloc(struct cgroup *cgrp) +static struct cgroup_subsys_state * +cgrp_css_alloc(struct cgroup_subsys_state *parent_css) { struct cgroup_subsys_state *css; @@ -137,16 +138,14 @@ static struct cgroup_subsys_state *cgrp_css_alloc(struct cgroup *cgrp) return css; } -static int cgrp_css_online(struct cgroup *cgrp) +static int cgrp_css_online(struct cgroup_subsys_state *css) { - struct cgroup_subsys_state *css = cgroup_css(cgrp, net_prio_subsys_id); - struct cgroup_subsys_state *parent_css; + struct cgroup_subsys_state *parent_css = css_parent(css); struct net_device *dev; int ret = 0; - if (!cgrp->parent) + if (!parent_css) return 0; - parent_css = cgroup_css(cgrp->parent, net_prio_subsys_id); rtnl_lock(); /* @@ -164,9 +163,9 @@ static int cgrp_css_online(struct cgroup *cgrp) return ret; } -static void cgrp_css_free(struct cgroup *cgrp) +static void cgrp_css_free(struct cgroup_subsys_state *css) { - kfree(cgroup_css(cgrp, net_prio_subsys_id)); + kfree(css); } static u64 read_prioidx(struct cgroup *cgrp, struct cftype *cft) @@ -221,12 +220,13 @@ static int update_netprio(const void *v, struct file *file, unsigned n) return 0; } -static void net_prio_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) +static void net_prio_attach(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset) { struct task_struct *p; void *v; - cgroup_taskset_for_each(p, cgrp, tset) { + cgroup_taskset_for_each(p, css->cgroup, tset) { task_lock(p); v = (void *)(unsigned long)task_netprioidx(p); iterate_fd(p->files, 0, update_netprio, v); diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 9e6b75e5efce..dc3983835893 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -38,7 +38,8 @@ static inline struct cgroup_cls_state *task_cls_state(struct task_struct *p) return css_cls_state(task_css(p, net_cls_subsys_id)); } -static struct cgroup_subsys_state *cgrp_css_alloc(struct cgroup *cgrp) +static struct cgroup_subsys_state * +cgrp_css_alloc(struct cgroup_subsys_state *parent_css) { struct cgroup_cls_state *cs; @@ -48,19 +49,19 @@ static struct cgroup_subsys_state *cgrp_css_alloc(struct cgroup *cgrp) return &cs->css; } -static int cgrp_css_online(struct cgroup *cgrp) +static int cgrp_css_online(struct cgroup_subsys_state *css) { - struct cgroup_cls_state *cs = cgrp_cls_state(cgrp); - struct cgroup_cls_state *parent = css_cls_state(css_parent(&cs->css)); + struct cgroup_cls_state *cs = css_cls_state(css); + struct cgroup_cls_state *parent = css_cls_state(css_parent(css)); if (parent) cs->classid = parent->classid; return 0; } -static void cgrp_css_free(struct cgroup *cgrp) +static void cgrp_css_free(struct cgroup_subsys_state *css) { - kfree(cgrp_cls_state(cgrp)); + kfree(css_cls_state(css)); } static int update_classid(const void *v, struct file *file, unsigned n) @@ -72,12 +73,13 @@ static int update_classid(const void *v, struct file *file, unsigned n) return 0; } -static void cgrp_attach(struct cgroup *cgrp, struct cgroup_taskset *tset) +static void cgrp_attach(struct cgroup_subsys_state *css, + struct cgroup_taskset *tset) { struct task_struct *p; void *v; - cgroup_taskset_for_each(p, cgrp, tset) { + cgroup_taskset_for_each(p, css->cgroup, tset) { task_lock(p); v = (void *)(unsigned long)task_cls_classid(p); iterate_fd(p->files, 0, update_classid, v); diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 635a49db005d..7293ac49ba7b 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -68,7 +68,7 @@ static inline struct dev_cgroup *task_devcgroup(struct task_struct *task) struct cgroup_subsys devices_subsys; -static int devcgroup_can_attach(struct cgroup *new_cgrp, +static int devcgroup_can_attach(struct cgroup_subsys_state *new_css, struct cgroup_taskset *set) { struct task_struct *task = cgroup_taskset_first(set); @@ -193,13 +193,13 @@ static inline bool is_devcg_online(const struct dev_cgroup *devcg) /** * devcgroup_online - initializes devcgroup's behavior and exceptions based on * parent's - * @cgroup: cgroup getting online + * @css: css getting online * returns 0 in case of success, error code otherwise */ -static int devcgroup_online(struct cgroup *cgroup) +static int devcgroup_online(struct cgroup_subsys_state *css) { - struct dev_cgroup *dev_cgroup = cgroup_to_devcgroup(cgroup); - struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css_parent(&dev_cgroup->css)); + struct dev_cgroup *dev_cgroup = css_to_devcgroup(css); + struct dev_cgroup *parent_dev_cgroup = css_to_devcgroup(css_parent(css)); int ret = 0; mutex_lock(&devcgroup_mutex); @@ -217,9 +217,9 @@ static int devcgroup_online(struct cgroup *cgroup) return ret; } -static void devcgroup_offline(struct cgroup *cgroup) +static void devcgroup_offline(struct cgroup_subsys_state *css) { - struct dev_cgroup *dev_cgroup = cgroup_to_devcgroup(cgroup); + struct dev_cgroup *dev_cgroup = css_to_devcgroup(css); mutex_lock(&devcgroup_mutex); dev_cgroup->behavior = DEVCG_DEFAULT_NONE; @@ -229,7 +229,8 @@ static void devcgroup_offline(struct cgroup *cgroup) /* * called from kernel/cgroup.c with cgroup_lock() held. */ -static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup) +static struct cgroup_subsys_state * +devcgroup_css_alloc(struct cgroup_subsys_state *parent_css) { struct dev_cgroup *dev_cgroup; @@ -242,11 +243,10 @@ static struct cgroup_subsys_state *devcgroup_css_alloc(struct cgroup *cgroup) return &dev_cgroup->css; } -static void devcgroup_css_free(struct cgroup *cgroup) +static void devcgroup_css_free(struct cgroup_subsys_state *css) { - struct dev_cgroup *dev_cgroup; + struct dev_cgroup *dev_cgroup = css_to_devcgroup(css); - dev_cgroup = cgroup_to_devcgroup(cgroup); __dev_exception_clean(dev_cgroup); kfree(dev_cgroup); } -- cgit v1.2.3-71-gd317 From 2bb566cb68dfafad328af666ebadf0e49accd6ca Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:23 -0400 Subject: cgroup: add subsys backlink pointer to cftype cgroup is transitioning to using css (cgroup_subsys_state) instead of cgroup as the primary subsystem handle. The cgroupfs file interface will be converted to use css's which requires finding out the subsystem from cftype so that the matching css can be determined from the cgroup. This patch adds cftype->ss which points to the subsystem the file belongs to. The field is initialized while a cftype is being registered. This makes it unnecessary to explicitly specify the subsystem for other cftype handling functions. @ss argument dropped from various cftype handling functions. This patch shouldn't introduce any behavior differences. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Vivek Goyal Cc: Jens Axboe --- block/blk-cgroup.c | 2 +- include/linux/cgroup.h | 8 +++++- kernel/cgroup.c | 78 ++++++++++++++++++++++++++++---------------------- 3 files changed, 51 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 79fd9f4fadb7..34063739745b 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -1128,7 +1128,7 @@ void blkcg_policy_unregister(struct blkcg_policy *pol) /* kill the intf files first */ if (pol->cftypes) - cgroup_rm_cftypes(&blkio_subsys, pol->cftypes); + cgroup_rm_cftypes(pol->cftypes); /* unregister and update blkgs */ blkcg_policy[pol->plid] = NULL; diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 9c2b9dd9121d..5db8138a0482 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -429,6 +429,12 @@ struct cftype { /* CFTYPE_* flags */ unsigned int flags; + /* + * The subsys this file belongs to. Initialized automatically + * during registration. NULL for cgroup core files. + */ + struct cgroup_subsys *ss; + int (*open)(struct inode *inode, struct file *file); ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft, struct file *file, @@ -542,7 +548,7 @@ static inline const char *cgroup_name(const struct cgroup *cgrp) } int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); -int cgroup_rm_cftypes(struct cgroup_subsys *ss, struct cftype *cfts); +int cgroup_rm_cftypes(struct cftype *cfts); bool cgroup_is_descendant(struct cgroup *cgrp, struct cgroup *ancestor); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 271d9a5cde5f..c4bc8dac3b1d 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -219,8 +219,8 @@ static struct cftype cgroup_base_files[]; static void cgroup_offline_fn(struct work_struct *work); static int cgroup_destroy_locked(struct cgroup *cgrp); -static int cgroup_addrm_files(struct cgroup *cgrp, struct cgroup_subsys *subsys, - struct cftype cfts[], bool is_add); +static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], + bool is_add); /* convenient tests for these bits */ static inline bool cgroup_is_dead(const struct cgroup *cgrp) @@ -974,7 +974,7 @@ static void cgroup_clear_dir(struct cgroup *cgrp, unsigned long subsys_mask) if (!test_bit(i, &subsys_mask)) continue; list_for_each_entry(set, &ss->cftsets, node) - cgroup_addrm_files(cgrp, NULL, set->cfts, false); + cgroup_addrm_files(cgrp, set->cfts, false); } } @@ -1623,7 +1623,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type, */ cred = override_creds(&init_cred); - ret = cgroup_addrm_files(root_cgrp, NULL, cgroup_base_files, true); + ret = cgroup_addrm_files(root_cgrp, cgroup_base_files, true); if (ret) goto rm_base_files; @@ -1681,7 +1681,7 @@ static struct dentry *cgroup_mount(struct file_system_type *fs_type, rm_base_files: free_cgrp_cset_links(&tmp_links); - cgroup_addrm_files(&root->top_cgroup, NULL, cgroup_base_files, false); + cgroup_addrm_files(&root->top_cgroup, cgroup_base_files, false); revert_creds(cred); unlock_drop: cgroup_exit_root_id(root); @@ -2694,8 +2694,7 @@ static umode_t cgroup_file_mode(const struct cftype *cft) return mode; } -static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys, - struct cftype *cft) +static int cgroup_add_file(struct cgroup *cgrp, struct cftype *cft) { struct dentry *dir = cgrp->dentry; struct cgroup *parent = __d_cgrp(dir); @@ -2705,8 +2704,8 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cgroup_subsys *subsys, umode_t mode; char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 }; - if (subsys && !(cgrp->root->flags & CGRP_ROOT_NOPREFIX)) { - strcpy(name, subsys->name); + if (cft->ss && !(cgrp->root->flags & CGRP_ROOT_NOPREFIX)) { + strcpy(name, cft->ss->name); strcat(name, "."); } strcat(name, cft->name); @@ -2743,17 +2742,16 @@ out: /** * cgroup_addrm_files - add or remove files to a cgroup directory * @cgrp: the target cgroup - * @subsys: the subsystem of files to be added * @cfts: array of cftypes to be added * @is_add: whether to add or remove * * Depending on @is_add, add or remove files defined by @cfts on @cgrp. - * All @cfts should belong to @subsys. For removals, this function never - * fails. If addition fails, this function doesn't remove files already - * added. The caller is responsible for cleaning up. + * For removals, this function never fails. If addition fails, this + * function doesn't remove files already added. The caller is responsible + * for cleaning up. */ -static int cgroup_addrm_files(struct cgroup *cgrp, struct cgroup_subsys *subsys, - struct cftype cfts[], bool is_add) +static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], + bool is_add) { struct cftype *cft; int ret; @@ -2771,7 +2769,7 @@ static int cgroup_addrm_files(struct cgroup *cgrp, struct cgroup_subsys *subsys, continue; if (is_add) { - ret = cgroup_add_file(cgrp, subsys, cft); + ret = cgroup_add_file(cgrp, cft); if (ret) { pr_warn("cgroup_addrm_files: failed to add %s, err=%d\n", cft->name, ret); @@ -2796,11 +2794,11 @@ static void cgroup_cfts_prepare(void) mutex_lock(&cgroup_mutex); } -static int cgroup_cfts_commit(struct cgroup_subsys *ss, - struct cftype *cfts, bool is_add) +static int cgroup_cfts_commit(struct cftype *cfts, bool is_add) __releases(&cgroup_mutex) { LIST_HEAD(pending); + struct cgroup_subsys *ss = cfts[0].ss; struct cgroup *cgrp, *root = &ss->root->top_cgroup; struct super_block *sb = ss->root->sb; struct dentry *prev = NULL; @@ -2828,7 +2826,7 @@ static int cgroup_cfts_commit(struct cgroup_subsys *ss, inode = root->dentry->d_inode; mutex_lock(&inode->i_mutex); mutex_lock(&cgroup_mutex); - ret = cgroup_addrm_files(root, ss, cfts, is_add); + ret = cgroup_addrm_files(root, cfts, is_add); mutex_unlock(&cgroup_mutex); mutex_unlock(&inode->i_mutex); @@ -2851,7 +2849,7 @@ static int cgroup_cfts_commit(struct cgroup_subsys *ss, mutex_lock(&inode->i_mutex); mutex_lock(&cgroup_mutex); if (cgrp->serial_nr < update_before && !cgroup_is_dead(cgrp)) - ret = cgroup_addrm_files(cgrp, ss, cfts, is_add); + ret = cgroup_addrm_files(cgrp, cfts, is_add); mutex_unlock(&cgroup_mutex); mutex_unlock(&inode->i_mutex); @@ -2883,51 +2881,56 @@ out_deact: int cgroup_add_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) { struct cftype_set *set; + struct cftype *cft; int ret; set = kzalloc(sizeof(*set), GFP_KERNEL); if (!set) return -ENOMEM; + for (cft = cfts; cft->name[0] != '\0'; cft++) + cft->ss = ss; + cgroup_cfts_prepare(); set->cfts = cfts; list_add_tail(&set->node, &ss->cftsets); - ret = cgroup_cfts_commit(ss, cfts, true); + ret = cgroup_cfts_commit(cfts, true); if (ret) - cgroup_rm_cftypes(ss, cfts); + cgroup_rm_cftypes(cfts); return ret; } EXPORT_SYMBOL_GPL(cgroup_add_cftypes); /** * cgroup_rm_cftypes - remove an array of cftypes from a subsystem - * @ss: target cgroup subsystem * @cfts: zero-length name terminated array of cftypes * - * Unregister @cfts from @ss. Files described by @cfts are removed from - * all existing cgroups to which @ss is attached and all future cgroups - * won't have them either. This function can be called anytime whether @ss - * is attached or not. + * Unregister @cfts. Files described by @cfts are removed from all + * existing cgroups and all future cgroups won't have them either. This + * function can be called anytime whether @cfts' subsys is attached or not. * * Returns 0 on successful unregistration, -ENOENT if @cfts is not - * registered with @ss. + * registered. */ -int cgroup_rm_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) +int cgroup_rm_cftypes(struct cftype *cfts) { struct cftype_set *set; + if (!cfts || !cfts[0].ss) + return -ENOENT; + cgroup_cfts_prepare(); - list_for_each_entry(set, &ss->cftsets, node) { + list_for_each_entry(set, &cfts[0].ss->cftsets, node) { if (set->cfts == cfts) { list_del(&set->node); kfree(set); - cgroup_cfts_commit(ss, cfts, false); + cgroup_cfts_commit(cfts, false); return 0; } } - cgroup_cfts_commit(ss, NULL, false); + cgroup_cfts_commit(NULL, false); return -ENOENT; } @@ -4148,7 +4151,7 @@ static int cgroup_populate_dir(struct cgroup *cgrp, unsigned long subsys_mask) continue; list_for_each_entry(set, &ss->cftsets, node) { - ret = cgroup_addrm_files(cgrp, ss, set->cfts, true); + ret = cgroup_addrm_files(cgrp, set->cfts, true); if (ret < 0) goto err; } @@ -4377,7 +4380,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, idr_replace(&root->cgroup_idr, cgrp, cgrp->id); - err = cgroup_addrm_files(cgrp, NULL, cgroup_base_files, true); + err = cgroup_addrm_files(cgrp, cgroup_base_files, true); if (err) goto err_destroy; @@ -4538,7 +4541,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) * but we aren't quite done with @cgrp yet, so hold onto it. */ cgroup_clear_dir(cgrp, cgrp->root->subsys_mask); - cgroup_addrm_files(cgrp, NULL, cgroup_base_files, false); + cgroup_addrm_files(cgrp, cgroup_base_files, false); dget(d); cgroup_d_remove_dir(d); @@ -4632,6 +4635,11 @@ static void __init_or_module cgroup_init_cftsets(struct cgroup_subsys *ss) * deregistration. */ if (ss->base_cftypes) { + struct cftype *cft; + + for (cft = ss->base_cftypes; cft->name[0] != '\0'; cft++) + cft->ss = ss; + ss->base_cftset.cfts = ss->base_cftypes; list_add_tail(&ss->base_cftset.node, &ss->cftsets); } -- cgit v1.2.3-71-gd317 From 67f4c36f83455b253445b2cb28ac9a2c4f85d99a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:24 -0400 Subject: cgroup: add cgroup->dummy_css cgroup subsystem API is being converted to use css (cgroup_subsys_state) as the main handle, which makes things a bit awkward for subsystem agnostic core features - the "cgroup.*" interface files and various iterations - a bit awkward as they don't have a css to use. This patch adds cgroup->dummy_css which has NULL ->ss and whose only role is pointing back to the cgroup. This will be used to support subsystem agnostic features on the coming css based API. css_parent() is updated to handle dummy_css's. Note that css will soon grow its own ->parent field and css_parent() will be made trivial. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 11 ++++++++++- kernel/cgroup.c | 9 +++++---- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 5db8138a0482..b0d5f53ae5e1 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -225,6 +225,9 @@ struct cgroup { struct list_head pidlists; struct mutex pidlist_mutex; + /* dummy css with NULL ->ss, points back to this cgroup */ + struct cgroup_subsys_state dummy_css; + /* For css percpu_ref killing and RCU-protected deletion */ struct rcu_head rcu_head; struct work_struct destroy_work; @@ -668,7 +671,13 @@ struct cgroup_subsys_state *css_parent(struct cgroup_subsys_state *css) { struct cgroup *parent_cgrp = css->cgroup->parent; - return parent_cgrp ? parent_cgrp->subsys[css->ss->subsys_id] : NULL; + if (!parent_cgrp) + return NULL; + + if (css->ss) + return parent_cgrp->subsys[css->ss->subsys_id]; + else + return &parent_cgrp->dummy_css; } /** diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 583f8f66a7e1..c049992f1ffa 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1365,6 +1365,7 @@ static void init_cgroup_housekeeping(struct cgroup *cgrp) INIT_LIST_HEAD(&cgrp->release_list); INIT_LIST_HEAD(&cgrp->pidlists); mutex_init(&cgrp->pidlist_mutex); + cgrp->dummy_css.cgroup = cgrp; INIT_LIST_HEAD(&cgrp->event_list); spin_lock_init(&cgrp->event_list_lock); simple_xattrs_init(&cgrp->xattrs); @@ -2285,7 +2286,7 @@ static struct cgroup_subsys_state *cgroup_file_css(struct cfent *cfe) if (cft->ss) return cgrp->subsys[cft->ss->subsys_id]; - return NULL; + return &cgrp->dummy_css; } /* A buffer size big enough for numbers or short strings */ @@ -2467,7 +2468,7 @@ static int cgroup_file_open(struct inode *inode, struct file *file) * unpinned either on open failure or release. This ensures that * @css stays alive for all file operations. */ - if (css && !css_tryget(css)) + if (css->ss && !css_tryget(css)) return -ENODEV; if (cft->read_map || cft->read_seq_string) { @@ -2477,7 +2478,7 @@ static int cgroup_file_open(struct inode *inode, struct file *file) err = cft->open(inode, file); } - if (css && err) + if (css->ss && err) css_put(css); return err; } @@ -2491,7 +2492,7 @@ static int cgroup_file_release(struct inode *inode, struct file *file) if (cft->release) ret = cft->release(inode, file); - if (css) + if (css->ss) css_put(css); return ret; } -- cgit v1.2.3-71-gd317 From 182446d087906de40e514573a92a97b203695f71 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:24 -0400 Subject: cgroup: pass around cgroup_subsys_state instead of cgroup in file methods cgroup is currently in the process of transitioning to using struct cgroup_subsys_state * as the primary handle instead of struct cgroup. Please see the previous commit which converts the subsystem methods for rationale. This patch converts all cftype file operations to take @css instead of @cgroup. cftypes for the cgroup core files don't have their subsytem pointer set. These will automatically use the dummy_css added by the previous patch and can be converted the same way. Most subsystem conversions are straight forwards but there are some interesting ones. * freezer: update_if_frozen() is also converted to take @css instead of @cgroup for consistency. This will make the code look simpler too once iterators are converted to use css. * memory/vmpressure: mem_cgroup_from_css() needs to be exported to vmpressure while mem_cgroup_from_cont() can be made static. Updated accordingly. * cpu: cgroup_tg() doesn't have any user left. Removed. * cpuacct: cgroup_ca() doesn't have any user left. Removed. * hugetlb: hugetlb_cgroup_form_cgroup() doesn't have any user left. Removed. * net_cls: cgrp_cls_state() doesn't have any user left. Removed. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Michal Hocko Acked-by: Vivek Goyal Acked-by: Aristeu Rozanski Acked-by: Daniel Wagner Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Johannes Weiner Cc: Balbir Singh Cc: Matt Helsley Cc: Jens Axboe Cc: Steven Rostedt --- block/blk-cgroup.c | 6 +- block/blk-throttle.c | 32 ++++----- block/cfq-iosched.c | 90 ++++++++++++------------- include/linux/cgroup.h | 24 ++++--- include/linux/memcontrol.h | 2 +- kernel/cgroup.c | 162 +++++++++++++++++++++++---------------------- kernel/cgroup_freezer.c | 40 +++++------ kernel/cpuset.c | 35 +++++----- kernel/sched/core.c | 65 +++++++++--------- kernel/sched/cpuacct.c | 28 +++----- mm/hugetlb_cgroup.c | 26 +++----- mm/memcontrol.c | 88 ++++++++++++------------ mm/vmpressure.c | 4 +- net/core/netprio_cgroup.c | 10 ++- net/ipv4/tcp_memcontrol.c | 12 ++-- net/sched/cls_cgroup.c | 14 ++-- security/device_cgroup.c | 12 ++-- 17 files changed, 322 insertions(+), 328 deletions(-) (limited to 'include') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 34063739745b..f46f3c69179c 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -437,10 +437,10 @@ struct request_list *__blk_queue_next_rl(struct request_list *rl, return &blkg->rl; } -static int blkcg_reset_stats(struct cgroup *cgroup, struct cftype *cftype, - u64 val) +static int blkcg_reset_stats(struct cgroup_subsys_state *css, + struct cftype *cftype, u64 val) { - struct blkcg *blkcg = cgroup_to_blkcg(cgroup); + struct blkcg *blkcg = css_to_blkcg(css); struct blkcg_gq *blkg; int i; diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 08a32dfd3844..88bcfb651b0b 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -1293,10 +1293,10 @@ static u64 tg_prfill_cpu_rwstat(struct seq_file *sf, return __blkg_prfill_rwstat(sf, pd, &rwstat); } -static int tg_print_cpu_rwstat(struct cgroup *cgrp, struct cftype *cft, - struct seq_file *sf) +static int tg_print_cpu_rwstat(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *sf) { - struct blkcg *blkcg = cgroup_to_blkcg(cgrp); + struct blkcg *blkcg = css_to_blkcg(css); blkcg_print_blkgs(sf, blkcg, tg_prfill_cpu_rwstat, &blkcg_policy_throtl, cft->private, true); @@ -1325,26 +1325,26 @@ static u64 tg_prfill_conf_uint(struct seq_file *sf, struct blkg_policy_data *pd, return __blkg_prfill_u64(sf, pd, v); } -static int tg_print_conf_u64(struct cgroup *cgrp, struct cftype *cft, - struct seq_file *sf) +static int tg_print_conf_u64(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *sf) { - blkcg_print_blkgs(sf, cgroup_to_blkcg(cgrp), tg_prfill_conf_u64, + blkcg_print_blkgs(sf, css_to_blkcg(css), tg_prfill_conf_u64, &blkcg_policy_throtl, cft->private, false); return 0; } -static int tg_print_conf_uint(struct cgroup *cgrp, struct cftype *cft, - struct seq_file *sf) +static int tg_print_conf_uint(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *sf) { - blkcg_print_blkgs(sf, cgroup_to_blkcg(cgrp), tg_prfill_conf_uint, + blkcg_print_blkgs(sf, css_to_blkcg(css), tg_prfill_conf_uint, &blkcg_policy_throtl, cft->private, false); return 0; } -static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, - bool is_u64) +static int tg_set_conf(struct cgroup_subsys_state *css, struct cftype *cft, + const char *buf, bool is_u64) { - struct blkcg *blkcg = cgroup_to_blkcg(cgrp); + struct blkcg *blkcg = css_to_blkcg(css); struct blkg_conf_ctx ctx; struct throtl_grp *tg; struct throtl_service_queue *sq; @@ -1403,16 +1403,16 @@ static int tg_set_conf(struct cgroup *cgrp, struct cftype *cft, const char *buf, return 0; } -static int tg_set_conf_u64(struct cgroup *cgrp, struct cftype *cft, +static int tg_set_conf_u64(struct cgroup_subsys_state *css, struct cftype *cft, const char *buf) { - return tg_set_conf(cgrp, cft, buf, true); + return tg_set_conf(css, cft, buf, true); } -static int tg_set_conf_uint(struct cgroup *cgrp, struct cftype *cft, +static int tg_set_conf_uint(struct cgroup_subsys_state *css, struct cftype *cft, const char *buf) { - return tg_set_conf(cgrp, cft, buf, false); + return tg_set_conf(css, cft, buf, false); } static struct cftype throtl_files[] = { diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index d5bbdcfd0dab..dabb9d02cf9a 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -1607,12 +1607,11 @@ static u64 cfqg_prfill_weight_device(struct seq_file *sf, return __blkg_prfill_u64(sf, pd, cfqg->dev_weight); } -static int cfqg_print_weight_device(struct cgroup *cgrp, struct cftype *cft, - struct seq_file *sf) +static int cfqg_print_weight_device(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *sf) { - blkcg_print_blkgs(sf, cgroup_to_blkcg(cgrp), - cfqg_prfill_weight_device, &blkcg_policy_cfq, 0, - false); + blkcg_print_blkgs(sf, css_to_blkcg(css), cfqg_prfill_weight_device, + &blkcg_policy_cfq, 0, false); return 0; } @@ -1626,35 +1625,34 @@ static u64 cfqg_prfill_leaf_weight_device(struct seq_file *sf, return __blkg_prfill_u64(sf, pd, cfqg->dev_leaf_weight); } -static int cfqg_print_leaf_weight_device(struct cgroup *cgrp, +static int cfqg_print_leaf_weight_device(struct cgroup_subsys_state *css, struct cftype *cft, struct seq_file *sf) { - blkcg_print_blkgs(sf, cgroup_to_blkcg(cgrp), - cfqg_prfill_leaf_weight_device, &blkcg_policy_cfq, 0, - false); + blkcg_print_blkgs(sf, css_to_blkcg(css), cfqg_prfill_leaf_weight_device, + &blkcg_policy_cfq, 0, false); return 0; } -static int cfq_print_weight(struct cgroup *cgrp, struct cftype *cft, +static int cfq_print_weight(struct cgroup_subsys_state *css, struct cftype *cft, struct seq_file *sf) { - seq_printf(sf, "%u\n", cgroup_to_blkcg(cgrp)->cfq_weight); + seq_printf(sf, "%u\n", css_to_blkcg(css)->cfq_weight); return 0; } -static int cfq_print_leaf_weight(struct cgroup *cgrp, struct cftype *cft, - struct seq_file *sf) +static int cfq_print_leaf_weight(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *sf) { - seq_printf(sf, "%u\n", - cgroup_to_blkcg(cgrp)->cfq_leaf_weight); + seq_printf(sf, "%u\n", css_to_blkcg(css)->cfq_leaf_weight); return 0; } -static int __cfqg_set_weight_device(struct cgroup *cgrp, struct cftype *cft, - const char *buf, bool is_leaf_weight) +static int __cfqg_set_weight_device(struct cgroup_subsys_state *css, + struct cftype *cft, const char *buf, + bool is_leaf_weight) { - struct blkcg *blkcg = cgroup_to_blkcg(cgrp); + struct blkcg *blkcg = css_to_blkcg(css); struct blkg_conf_ctx ctx; struct cfq_group *cfqg; int ret; @@ -1680,22 +1678,22 @@ static int __cfqg_set_weight_device(struct cgroup *cgrp, struct cftype *cft, return ret; } -static int cfqg_set_weight_device(struct cgroup *cgrp, struct cftype *cft, - const char *buf) +static int cfqg_set_weight_device(struct cgroup_subsys_state *css, + struct cftype *cft, const char *buf) { - return __cfqg_set_weight_device(cgrp, cft, buf, false); + return __cfqg_set_weight_device(css, cft, buf, false); } -static int cfqg_set_leaf_weight_device(struct cgroup *cgrp, struct cftype *cft, - const char *buf) +static int cfqg_set_leaf_weight_device(struct cgroup_subsys_state *css, + struct cftype *cft, const char *buf) { - return __cfqg_set_weight_device(cgrp, cft, buf, true); + return __cfqg_set_weight_device(css, cft, buf, true); } -static int __cfq_set_weight(struct cgroup *cgrp, struct cftype *cft, u64 val, - bool is_leaf_weight) +static int __cfq_set_weight(struct cgroup_subsys_state *css, struct cftype *cft, + u64 val, bool is_leaf_weight) { - struct blkcg *blkcg = cgroup_to_blkcg(cgrp); + struct blkcg *blkcg = css_to_blkcg(css); struct blkcg_gq *blkg; if (val < CFQ_WEIGHT_MIN || val > CFQ_WEIGHT_MAX) @@ -1727,30 +1725,32 @@ static int __cfq_set_weight(struct cgroup *cgrp, struct cftype *cft, u64 val, return 0; } -static int cfq_set_weight(struct cgroup *cgrp, struct cftype *cft, u64 val) +static int cfq_set_weight(struct cgroup_subsys_state *css, struct cftype *cft, + u64 val) { - return __cfq_set_weight(cgrp, cft, val, false); + return __cfq_set_weight(css, cft, val, false); } -static int cfq_set_leaf_weight(struct cgroup *cgrp, struct cftype *cft, u64 val) +static int cfq_set_leaf_weight(struct cgroup_subsys_state *css, + struct cftype *cft, u64 val) { - return __cfq_set_weight(cgrp, cft, val, true); + return __cfq_set_weight(css, cft, val, true); } -static int cfqg_print_stat(struct cgroup *cgrp, struct cftype *cft, +static int cfqg_print_stat(struct cgroup_subsys_state *css, struct cftype *cft, struct seq_file *sf) { - struct blkcg *blkcg = cgroup_to_blkcg(cgrp); + struct blkcg *blkcg = css_to_blkcg(css); blkcg_print_blkgs(sf, blkcg, blkg_prfill_stat, &blkcg_policy_cfq, cft->private, false); return 0; } -static int cfqg_print_rwstat(struct cgroup *cgrp, struct cftype *cft, - struct seq_file *sf) +static int cfqg_print_rwstat(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *sf) { - struct blkcg *blkcg = cgroup_to_blkcg(cgrp); + struct blkcg *blkcg = css_to_blkcg(css); blkcg_print_blkgs(sf, blkcg, blkg_prfill_rwstat, &blkcg_policy_cfq, cft->private, true); @@ -1773,20 +1773,20 @@ static u64 cfqg_prfill_rwstat_recursive(struct seq_file *sf, return __blkg_prfill_rwstat(sf, pd, &sum); } -static int cfqg_print_stat_recursive(struct cgroup *cgrp, struct cftype *cft, - struct seq_file *sf) +static int cfqg_print_stat_recursive(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *sf) { - struct blkcg *blkcg = cgroup_to_blkcg(cgrp); + struct blkcg *blkcg = css_to_blkcg(css); blkcg_print_blkgs(sf, blkcg, cfqg_prfill_stat_recursive, &blkcg_policy_cfq, cft->private, false); return 0; } -static int cfqg_print_rwstat_recursive(struct cgroup *cgrp, struct cftype *cft, - struct seq_file *sf) +static int cfqg_print_rwstat_recursive(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *sf) { - struct blkcg *blkcg = cgroup_to_blkcg(cgrp); + struct blkcg *blkcg = css_to_blkcg(css); blkcg_print_blkgs(sf, blkcg, cfqg_prfill_rwstat_recursive, &blkcg_policy_cfq, cft->private, true); @@ -1810,10 +1810,10 @@ static u64 cfqg_prfill_avg_queue_size(struct seq_file *sf, } /* print avg_queue_size */ -static int cfqg_print_avg_queue_size(struct cgroup *cgrp, struct cftype *cft, - struct seq_file *sf) +static int cfqg_print_avg_queue_size(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *sf) { - struct blkcg *blkcg = cgroup_to_blkcg(cgrp); + struct blkcg *blkcg = css_to_blkcg(css); blkcg_print_blkgs(sf, blkcg, cfqg_prfill_avg_queue_size, &blkcg_policy_cfq, 0, false); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index b0d5f53ae5e1..0b91436c68ef 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -439,34 +439,34 @@ struct cftype { struct cgroup_subsys *ss; int (*open)(struct inode *inode, struct file *file); - ssize_t (*read)(struct cgroup *cgrp, struct cftype *cft, + ssize_t (*read)(struct cgroup_subsys_state *css, struct cftype *cft, struct file *file, char __user *buf, size_t nbytes, loff_t *ppos); /* * read_u64() is a shortcut for the common case of returning a * single integer. Use it in place of read() */ - u64 (*read_u64)(struct cgroup *cgrp, struct cftype *cft); + u64 (*read_u64)(struct cgroup_subsys_state *css, struct cftype *cft); /* * read_s64() is a signed version of read_u64() */ - s64 (*read_s64)(struct cgroup *cgrp, struct cftype *cft); + s64 (*read_s64)(struct cgroup_subsys_state *css, struct cftype *cft); /* * read_map() is used for defining a map of key/value * pairs. It should call cb->fill(cb, key, value) for each * entry. The key/value pairs (and their ordering) should not * change between reboots. */ - int (*read_map)(struct cgroup *cgrp, struct cftype *cft, + int (*read_map)(struct cgroup_subsys_state *css, struct cftype *cft, struct cgroup_map_cb *cb); /* * read_seq_string() is used for outputting a simple sequence * using seqfile. */ - int (*read_seq_string)(struct cgroup *cgrp, struct cftype *cft, - struct seq_file *m); + int (*read_seq_string)(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *m); - ssize_t (*write)(struct cgroup *cgrp, struct cftype *cft, + ssize_t (*write)(struct cgroup_subsys_state *css, struct cftype *cft, struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos); @@ -475,18 +475,20 @@ struct cftype { * a single integer (as parsed by simple_strtoull) from * userspace. Use in place of write(); return 0 or error. */ - int (*write_u64)(struct cgroup *cgrp, struct cftype *cft, u64 val); + int (*write_u64)(struct cgroup_subsys_state *css, struct cftype *cft, + u64 val); /* * write_s64() is a signed version of write_u64() */ - int (*write_s64)(struct cgroup *cgrp, struct cftype *cft, s64 val); + int (*write_s64)(struct cgroup_subsys_state *css, struct cftype *cft, + s64 val); /* * write_string() is passed a nul-terminated kernelspace * buffer of maximum length determined by max_write_len. * Returns 0 or -ve error code. */ - int (*write_string)(struct cgroup *cgrp, struct cftype *cft, + int (*write_string)(struct cgroup_subsys_state *css, struct cftype *cft, const char *buffer); /* * trigger() callback can be used to get some kick from the @@ -494,7 +496,7 @@ struct cftype { * at all. The private field can be used to determine the * kick type for multiplexing. */ - int (*trigger)(struct cgroup *cgrp, unsigned int event); + int (*trigger)(struct cgroup_subsys_state *css, unsigned int event); int (*release)(struct inode *inode, struct file *file); diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 7b4d9d79570b..6c416092e324 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -85,7 +85,7 @@ extern struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p); extern struct mem_cgroup *try_get_mem_cgroup_from_mm(struct mm_struct *mm); extern struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg); -extern struct mem_cgroup *mem_cgroup_from_cont(struct cgroup *cont); +extern struct mem_cgroup *mem_cgroup_from_css(struct cgroup_subsys_state *css); static inline bool mm_match_cgroup(const struct mm_struct *mm, const struct mem_cgroup *memcg) diff --git a/kernel/cgroup.c b/kernel/cgroup.c index c049992f1ffa..6ee469837fda 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2235,34 +2235,38 @@ int cgroup_attach_task_all(struct task_struct *from, struct task_struct *tsk) } EXPORT_SYMBOL_GPL(cgroup_attach_task_all); -static int cgroup_tasks_write(struct cgroup *cgrp, struct cftype *cft, u64 pid) +static int cgroup_tasks_write(struct cgroup_subsys_state *css, + struct cftype *cft, u64 pid) { - return attach_task_by_pid(cgrp, pid, false); + return attach_task_by_pid(css->cgroup, pid, false); } -static int cgroup_procs_write(struct cgroup *cgrp, struct cftype *cft, u64 tgid) +static int cgroup_procs_write(struct cgroup_subsys_state *css, + struct cftype *cft, u64 tgid) { - return attach_task_by_pid(cgrp, tgid, true); + return attach_task_by_pid(css->cgroup, tgid, true); } -static int cgroup_release_agent_write(struct cgroup *cgrp, struct cftype *cft, - const char *buffer) +static int cgroup_release_agent_write(struct cgroup_subsys_state *css, + struct cftype *cft, const char *buffer) { - BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX); + BUILD_BUG_ON(sizeof(css->cgroup->root->release_agent_path) < PATH_MAX); if (strlen(buffer) >= PATH_MAX) return -EINVAL; - if (!cgroup_lock_live_group(cgrp)) + if (!cgroup_lock_live_group(css->cgroup)) return -ENODEV; mutex_lock(&cgroup_root_mutex); - strcpy(cgrp->root->release_agent_path, buffer); + strcpy(css->cgroup->root->release_agent_path, buffer); mutex_unlock(&cgroup_root_mutex); mutex_unlock(&cgroup_mutex); return 0; } -static int cgroup_release_agent_show(struct cgroup *cgrp, struct cftype *cft, - struct seq_file *seq) +static int cgroup_release_agent_show(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *seq) { + struct cgroup *cgrp = css->cgroup; + if (!cgroup_lock_live_group(cgrp)) return -ENODEV; seq_puts(seq, cgrp->root->release_agent_path); @@ -2271,10 +2275,10 @@ static int cgroup_release_agent_show(struct cgroup *cgrp, struct cftype *cft, return 0; } -static int cgroup_sane_behavior_show(struct cgroup *cgrp, struct cftype *cft, - struct seq_file *seq) +static int cgroup_sane_behavior_show(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *seq) { - seq_printf(seq, "%d\n", cgroup_sane_behavior(cgrp)); + seq_printf(seq, "%d\n", cgroup_sane_behavior(css->cgroup)); return 0; } @@ -2292,10 +2296,10 @@ static struct cgroup_subsys_state *cgroup_file_css(struct cfent *cfe) /* A buffer size big enough for numbers or short strings */ #define CGROUP_LOCAL_BUFFER_SIZE 64 -static ssize_t cgroup_write_X64(struct cgroup *cgrp, struct cftype *cft, - struct file *file, - const char __user *userbuf, - size_t nbytes, loff_t *unused_ppos) +static ssize_t cgroup_write_X64(struct cgroup_subsys_state *css, + struct cftype *cft, struct file *file, + const char __user *userbuf, size_t nbytes, + loff_t *unused_ppos) { char buffer[CGROUP_LOCAL_BUFFER_SIZE]; int retval = 0; @@ -2313,22 +2317,22 @@ static ssize_t cgroup_write_X64(struct cgroup *cgrp, struct cftype *cft, u64 val = simple_strtoull(strstrip(buffer), &end, 0); if (*end) return -EINVAL; - retval = cft->write_u64(cgrp, cft, val); + retval = cft->write_u64(css, cft, val); } else { s64 val = simple_strtoll(strstrip(buffer), &end, 0); if (*end) return -EINVAL; - retval = cft->write_s64(cgrp, cft, val); + retval = cft->write_s64(css, cft, val); } if (!retval) retval = nbytes; return retval; } -static ssize_t cgroup_write_string(struct cgroup *cgrp, struct cftype *cft, - struct file *file, - const char __user *userbuf, - size_t nbytes, loff_t *unused_ppos) +static ssize_t cgroup_write_string(struct cgroup_subsys_state *css, + struct cftype *cft, struct file *file, + const char __user *userbuf, size_t nbytes, + loff_t *unused_ppos) { char local_buffer[CGROUP_LOCAL_BUFFER_SIZE]; int retval = 0; @@ -2351,7 +2355,7 @@ static ssize_t cgroup_write_string(struct cgroup *cgrp, struct cftype *cft, } buffer[nbytes] = 0; /* nul-terminate */ - retval = cft->write_string(cgrp, cft, strstrip(buffer)); + retval = cft->write_string(css, cft, strstrip(buffer)); if (!retval) retval = nbytes; out: @@ -2361,60 +2365,60 @@ out: } static ssize_t cgroup_file_write(struct file *file, const char __user *buf, - size_t nbytes, loff_t *ppos) + size_t nbytes, loff_t *ppos) { + struct cfent *cfe = __d_cfe(file->f_dentry); struct cftype *cft = __d_cft(file->f_dentry); - struct cgroup *cgrp = __d_cgrp(file->f_dentry->d_parent); + struct cgroup_subsys_state *css = cgroup_file_css(cfe); if (cft->write) - return cft->write(cgrp, cft, file, buf, nbytes, ppos); + return cft->write(css, cft, file, buf, nbytes, ppos); if (cft->write_u64 || cft->write_s64) - return cgroup_write_X64(cgrp, cft, file, buf, nbytes, ppos); + return cgroup_write_X64(css, cft, file, buf, nbytes, ppos); if (cft->write_string) - return cgroup_write_string(cgrp, cft, file, buf, nbytes, ppos); + return cgroup_write_string(css, cft, file, buf, nbytes, ppos); if (cft->trigger) { - int ret = cft->trigger(cgrp, (unsigned int)cft->private); + int ret = cft->trigger(css, (unsigned int)cft->private); return ret ? ret : nbytes; } return -EINVAL; } -static ssize_t cgroup_read_u64(struct cgroup *cgrp, struct cftype *cft, - struct file *file, - char __user *buf, size_t nbytes, - loff_t *ppos) +static ssize_t cgroup_read_u64(struct cgroup_subsys_state *css, + struct cftype *cft, struct file *file, + char __user *buf, size_t nbytes, loff_t *ppos) { char tmp[CGROUP_LOCAL_BUFFER_SIZE]; - u64 val = cft->read_u64(cgrp, cft); + u64 val = cft->read_u64(css, cft); int len = sprintf(tmp, "%llu\n", (unsigned long long) val); return simple_read_from_buffer(buf, nbytes, ppos, tmp, len); } -static ssize_t cgroup_read_s64(struct cgroup *cgrp, struct cftype *cft, - struct file *file, - char __user *buf, size_t nbytes, - loff_t *ppos) +static ssize_t cgroup_read_s64(struct cgroup_subsys_state *css, + struct cftype *cft, struct file *file, + char __user *buf, size_t nbytes, loff_t *ppos) { char tmp[CGROUP_LOCAL_BUFFER_SIZE]; - s64 val = cft->read_s64(cgrp, cft); + s64 val = cft->read_s64(css, cft); int len = sprintf(tmp, "%lld\n", (long long) val); return simple_read_from_buffer(buf, nbytes, ppos, tmp, len); } static ssize_t cgroup_file_read(struct file *file, char __user *buf, - size_t nbytes, loff_t *ppos) + size_t nbytes, loff_t *ppos) { + struct cfent *cfe = __d_cfe(file->f_dentry); struct cftype *cft = __d_cft(file->f_dentry); - struct cgroup *cgrp = __d_cgrp(file->f_dentry->d_parent); + struct cgroup_subsys_state *css = cgroup_file_css(cfe); if (cft->read) - return cft->read(cgrp, cft, file, buf, nbytes, ppos); + return cft->read(css, cft, file, buf, nbytes, ppos); if (cft->read_u64) - return cgroup_read_u64(cgrp, cft, file, buf, nbytes, ppos); + return cgroup_read_u64(css, cft, file, buf, nbytes, ppos); if (cft->read_s64) - return cgroup_read_s64(cgrp, cft, file, buf, nbytes, ppos); + return cgroup_read_s64(css, cft, file, buf, nbytes, ppos); return -EINVAL; } @@ -2433,16 +2437,16 @@ static int cgroup_seqfile_show(struct seq_file *m, void *arg) { struct cfent *cfe = m->private; struct cftype *cft = cfe->type; - struct cgroup *cgrp = __d_cgrp(cfe->dentry->d_parent); + struct cgroup_subsys_state *css = cgroup_file_css(cfe); if (cft->read_map) { struct cgroup_map_cb cb = { .fill = cgroup_map_add, .state = m, }; - return cft->read_map(cgrp, cft, &cb); + return cft->read_map(css, cft, &cb); } - return cft->read_seq_string(cgrp, cft, m); + return cft->read_seq_string(css, cft, m); } static const struct file_operations cgroup_seqfile_operations = { @@ -3860,21 +3864,20 @@ static int cgroup_procs_open(struct inode *unused, struct file *file) return cgroup_pidlist_open(file, CGROUP_FILE_PROCS); } -static u64 cgroup_read_notify_on_release(struct cgroup *cgrp, - struct cftype *cft) +static u64 cgroup_read_notify_on_release(struct cgroup_subsys_state *css, + struct cftype *cft) { - return notify_on_release(cgrp); + return notify_on_release(css->cgroup); } -static int cgroup_write_notify_on_release(struct cgroup *cgrp, - struct cftype *cft, - u64 val) +static int cgroup_write_notify_on_release(struct cgroup_subsys_state *css, + struct cftype *cft, u64 val) { - clear_bit(CGRP_RELEASABLE, &cgrp->flags); + clear_bit(CGRP_RELEASABLE, &css->cgroup->flags); if (val) - set_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags); + set_bit(CGRP_NOTIFY_ON_RELEASE, &css->cgroup->flags); else - clear_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags); + clear_bit(CGRP_NOTIFY_ON_RELEASE, &css->cgroup->flags); return 0; } @@ -3972,9 +3975,10 @@ static void cgroup_event_ptable_queue_proc(struct file *file, * Input must be in format ' '. * Interpretation of args is defined by control file implementation. */ -static int cgroup_write_event_control(struct cgroup *cgrp, struct cftype *cft, - const char *buffer) +static int cgroup_write_event_control(struct cgroup_subsys_state *css, + struct cftype *cft, const char *buffer) { + struct cgroup *cgrp = css->cgroup; struct cgroup_event *event; struct cgroup *cgrp_cfile; unsigned int efd, cfd; @@ -4082,20 +4086,19 @@ out_kfree: return ret; } -static u64 cgroup_clone_children_read(struct cgroup *cgrp, - struct cftype *cft) +static u64 cgroup_clone_children_read(struct cgroup_subsys_state *css, + struct cftype *cft) { - return test_bit(CGRP_CPUSET_CLONE_CHILDREN, &cgrp->flags); + return test_bit(CGRP_CPUSET_CLONE_CHILDREN, &css->cgroup->flags); } -static int cgroup_clone_children_write(struct cgroup *cgrp, - struct cftype *cft, - u64 val) +static int cgroup_clone_children_write(struct cgroup_subsys_state *css, + struct cftype *cft, u64 val) { if (val) - set_bit(CGRP_CPUSET_CLONE_CHILDREN, &cgrp->flags); + set_bit(CGRP_CPUSET_CLONE_CHILDREN, &css->cgroup->flags); else - clear_bit(CGRP_CPUSET_CLONE_CHILDREN, &cgrp->flags); + clear_bit(CGRP_CPUSET_CLONE_CHILDREN, &css->cgroup->flags); return 0; } @@ -5585,17 +5588,19 @@ static void debug_css_free(struct cgroup_subsys_state *css) kfree(css); } -static u64 debug_taskcount_read(struct cgroup *cgrp, struct cftype *cft) +static u64 debug_taskcount_read(struct cgroup_subsys_state *css, + struct cftype *cft) { - return cgroup_task_count(cgrp); + return cgroup_task_count(css->cgroup); } -static u64 current_css_set_read(struct cgroup *cgrp, struct cftype *cft) +static u64 current_css_set_read(struct cgroup_subsys_state *css, + struct cftype *cft) { return (u64)(unsigned long)current->cgroups; } -static u64 current_css_set_refcount_read(struct cgroup *cgrp, +static u64 current_css_set_refcount_read(struct cgroup_subsys_state *css, struct cftype *cft) { u64 count; @@ -5606,7 +5611,7 @@ static u64 current_css_set_refcount_read(struct cgroup *cgrp, return count; } -static int current_css_set_cg_links_read(struct cgroup *cgrp, +static int current_css_set_cg_links_read(struct cgroup_subsys_state *css, struct cftype *cft, struct seq_file *seq) { @@ -5633,14 +5638,13 @@ static int current_css_set_cg_links_read(struct cgroup *cgrp, } #define MAX_TASKS_SHOWN_PER_CSS 25 -static int cgroup_css_links_read(struct cgroup *cgrp, - struct cftype *cft, - struct seq_file *seq) +static int cgroup_css_links_read(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *seq) { struct cgrp_cset_link *link; read_lock(&css_set_lock); - list_for_each_entry(link, &cgrp->cset_links, cset_link) { + list_for_each_entry(link, &css->cgroup->cset_links, cset_link) { struct css_set *cset = link->cset; struct task_struct *task; int count = 0; @@ -5659,9 +5663,9 @@ static int cgroup_css_links_read(struct cgroup *cgrp, return 0; } -static u64 releasable_read(struct cgroup *cgrp, struct cftype *cft) +static u64 releasable_read(struct cgroup_subsys_state *css, struct cftype *cft) { - return test_bit(CGRP_RELEASABLE, &cgrp->flags); + return test_bit(CGRP_RELEASABLE, &css->cgroup->flags); } static struct cftype debug_files[] = { diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index f03a85719c3c..19613ba51444 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -245,7 +245,7 @@ out: /** * update_if_frozen - update whether a cgroup finished freezing - * @cgroup: cgroup of interest + * @css: css of interest * * Once FREEZING is initiated, transition to FROZEN is lazily updated by * calling this function. If the current state is FREEZING but not FROZEN, @@ -256,12 +256,12 @@ out: * update_if_frozen() on all descendants prior to invoking this function. * * Task states and freezer state might disagree while tasks are being - * migrated into or out of @cgroup, so we can't verify task states against + * migrated into or out of @css, so we can't verify task states against * @freezer state here. See freezer_attach() for details. */ -static void update_if_frozen(struct cgroup *cgroup) +static void update_if_frozen(struct cgroup_subsys_state *css) { - struct freezer *freezer = cgroup_freezer(cgroup); + struct freezer *freezer = css_freezer(css); struct cgroup *pos; struct cgroup_iter it; struct task_struct *task; @@ -275,7 +275,7 @@ static void update_if_frozen(struct cgroup *cgroup) goto out_unlock; /* are all (live) children frozen? */ - cgroup_for_each_child(pos, cgroup) { + cgroup_for_each_child(pos, css->cgroup) { struct freezer *child = cgroup_freezer(pos); if ((child->state & CGROUP_FREEZER_ONLINE) && @@ -284,9 +284,9 @@ static void update_if_frozen(struct cgroup *cgroup) } /* are all tasks frozen? */ - cgroup_iter_start(cgroup, &it); + cgroup_iter_start(css->cgroup, &it); - while ((task = cgroup_iter_next(cgroup, &it))) { + while ((task = cgroup_iter_next(css->cgroup, &it))) { if (freezing(task)) { /* * freezer_should_skip() indicates that the task @@ -301,12 +301,12 @@ static void update_if_frozen(struct cgroup *cgroup) freezer->state |= CGROUP_FROZEN; out_iter_end: - cgroup_iter_end(cgroup, &it); + cgroup_iter_end(css->cgroup, &it); out_unlock: spin_unlock_irq(&freezer->lock); } -static int freezer_read(struct cgroup *cgroup, struct cftype *cft, +static int freezer_read(struct cgroup_subsys_state *css, struct cftype *cft, struct seq_file *m) { struct cgroup *pos; @@ -314,13 +314,13 @@ static int freezer_read(struct cgroup *cgroup, struct cftype *cft, rcu_read_lock(); /* update states bottom-up */ - cgroup_for_each_descendant_post(pos, cgroup) - update_if_frozen(pos); - update_if_frozen(cgroup); + cgroup_for_each_descendant_post(pos, css->cgroup) + update_if_frozen(cgroup_css(pos, freezer_subsys_id)); + update_if_frozen(css); rcu_read_unlock(); - seq_puts(m, freezer_state_strs(cgroup_freezer(cgroup)->state)); + seq_puts(m, freezer_state_strs(css_freezer(css)->state)); seq_putc(m, '\n'); return 0; } @@ -426,7 +426,7 @@ static void freezer_change_state(struct freezer *freezer, bool freeze) rcu_read_unlock(); } -static int freezer_write(struct cgroup *cgroup, struct cftype *cft, +static int freezer_write(struct cgroup_subsys_state *css, struct cftype *cft, const char *buffer) { bool freeze; @@ -438,20 +438,22 @@ static int freezer_write(struct cgroup *cgroup, struct cftype *cft, else return -EINVAL; - freezer_change_state(cgroup_freezer(cgroup), freeze); + freezer_change_state(css_freezer(css), freeze); return 0; } -static u64 freezer_self_freezing_read(struct cgroup *cgroup, struct cftype *cft) +static u64 freezer_self_freezing_read(struct cgroup_subsys_state *css, + struct cftype *cft) { - struct freezer *freezer = cgroup_freezer(cgroup); + struct freezer *freezer = css_freezer(css); return (bool)(freezer->state & CGROUP_FREEZING_SELF); } -static u64 freezer_parent_freezing_read(struct cgroup *cgroup, struct cftype *cft) +static u64 freezer_parent_freezing_read(struct cgroup_subsys_state *css, + struct cftype *cft) { - struct freezer *freezer = cgroup_freezer(cgroup); + struct freezer *freezer = css_freezer(css); return (bool)(freezer->state & CGROUP_FREEZING_PARENT); } diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 8ce3fdc3dfcc..89b76e1d3aa1 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -1603,9 +1603,10 @@ typedef enum { FILE_SPREAD_SLAB, } cpuset_filetype_t; -static int cpuset_write_u64(struct cgroup *cgrp, struct cftype *cft, u64 val) +static int cpuset_write_u64(struct cgroup_subsys_state *css, struct cftype *cft, + u64 val) { - struct cpuset *cs = cgroup_cs(cgrp); + struct cpuset *cs = css_cs(css); cpuset_filetype_t type = cft->private; int retval = -ENODEV; @@ -1650,9 +1651,10 @@ out_unlock: return retval; } -static int cpuset_write_s64(struct cgroup *cgrp, struct cftype *cft, s64 val) +static int cpuset_write_s64(struct cgroup_subsys_state *css, struct cftype *cft, + s64 val) { - struct cpuset *cs = cgroup_cs(cgrp); + struct cpuset *cs = css_cs(css); cpuset_filetype_t type = cft->private; int retval = -ENODEV; @@ -1676,10 +1678,10 @@ out_unlock: /* * Common handling for a write to a "cpus" or "mems" file. */ -static int cpuset_write_resmask(struct cgroup *cgrp, struct cftype *cft, - const char *buf) +static int cpuset_write_resmask(struct cgroup_subsys_state *css, + struct cftype *cft, const char *buf) { - struct cpuset *cs = cgroup_cs(cgrp); + struct cpuset *cs = css_cs(css); struct cpuset *trialcs; int retval = -ENODEV; @@ -1758,13 +1760,12 @@ static size_t cpuset_sprintf_memlist(char *page, struct cpuset *cs) return count; } -static ssize_t cpuset_common_file_read(struct cgroup *cgrp, - struct cftype *cft, - struct file *file, - char __user *buf, - size_t nbytes, loff_t *ppos) +static ssize_t cpuset_common_file_read(struct cgroup_subsys_state *css, + struct cftype *cft, struct file *file, + char __user *buf, size_t nbytes, + loff_t *ppos) { - struct cpuset *cs = cgroup_cs(cgrp); + struct cpuset *cs = css_cs(css); cpuset_filetype_t type = cft->private; char *page; ssize_t retval = 0; @@ -1794,9 +1795,9 @@ out: return retval; } -static u64 cpuset_read_u64(struct cgroup *cgrp, struct cftype *cft) +static u64 cpuset_read_u64(struct cgroup_subsys_state *css, struct cftype *cft) { - struct cpuset *cs = cgroup_cs(cgrp); + struct cpuset *cs = css_cs(css); cpuset_filetype_t type = cft->private; switch (type) { case FILE_CPU_EXCLUSIVE: @@ -1825,9 +1826,9 @@ static u64 cpuset_read_u64(struct cgroup *cgrp, struct cftype *cft) return 0; } -static s64 cpuset_read_s64(struct cgroup *cgrp, struct cftype *cft) +static s64 cpuset_read_s64(struct cgroup_subsys_state *css, struct cftype *cft) { - struct cpuset *cs = cgroup_cs(cgrp); + struct cpuset *cs = css_cs(css); cpuset_filetype_t type = cft->private; switch (type) { case FILE_SCHED_RELAX_DOMAIN_LEVEL: diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 622b7efc5ade..cc9a49266382 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7088,12 +7088,6 @@ static inline struct task_group *css_tg(struct cgroup_subsys_state *css) return css ? container_of(css, struct task_group, css) : NULL; } -/* return corresponding task_group object of a cgroup */ -static inline struct task_group *cgroup_tg(struct cgroup *cgrp) -{ - return css_tg(cgroup_css(cgrp, cpu_cgroup_subsys_id)); -} - static struct cgroup_subsys_state * cpu_cgroup_css_alloc(struct cgroup_subsys_state *parent_css) { @@ -7179,15 +7173,16 @@ static void cpu_cgroup_exit(struct cgroup_subsys_state *css, } #ifdef CONFIG_FAIR_GROUP_SCHED -static int cpu_shares_write_u64(struct cgroup *cgrp, struct cftype *cftype, - u64 shareval) +static int cpu_shares_write_u64(struct cgroup_subsys_state *css, + struct cftype *cftype, u64 shareval) { - return sched_group_set_shares(cgroup_tg(cgrp), scale_load(shareval)); + return sched_group_set_shares(css_tg(css), scale_load(shareval)); } -static u64 cpu_shares_read_u64(struct cgroup *cgrp, struct cftype *cft) +static u64 cpu_shares_read_u64(struct cgroup_subsys_state *css, + struct cftype *cft) { - struct task_group *tg = cgroup_tg(cgrp); + struct task_group *tg = css_tg(css); return (u64) scale_load_down(tg->shares); } @@ -7309,26 +7304,28 @@ long tg_get_cfs_period(struct task_group *tg) return cfs_period_us; } -static s64 cpu_cfs_quota_read_s64(struct cgroup *cgrp, struct cftype *cft) +static s64 cpu_cfs_quota_read_s64(struct cgroup_subsys_state *css, + struct cftype *cft) { - return tg_get_cfs_quota(cgroup_tg(cgrp)); + return tg_get_cfs_quota(css_tg(css)); } -static int cpu_cfs_quota_write_s64(struct cgroup *cgrp, struct cftype *cftype, - s64 cfs_quota_us) +static int cpu_cfs_quota_write_s64(struct cgroup_subsys_state *css, + struct cftype *cftype, s64 cfs_quota_us) { - return tg_set_cfs_quota(cgroup_tg(cgrp), cfs_quota_us); + return tg_set_cfs_quota(css_tg(css), cfs_quota_us); } -static u64 cpu_cfs_period_read_u64(struct cgroup *cgrp, struct cftype *cft) +static u64 cpu_cfs_period_read_u64(struct cgroup_subsys_state *css, + struct cftype *cft) { - return tg_get_cfs_period(cgroup_tg(cgrp)); + return tg_get_cfs_period(css_tg(css)); } -static int cpu_cfs_period_write_u64(struct cgroup *cgrp, struct cftype *cftype, - u64 cfs_period_us) +static int cpu_cfs_period_write_u64(struct cgroup_subsys_state *css, + struct cftype *cftype, u64 cfs_period_us) { - return tg_set_cfs_period(cgroup_tg(cgrp), cfs_period_us); + return tg_set_cfs_period(css_tg(css), cfs_period_us); } struct cfs_schedulable_data { @@ -7409,10 +7406,10 @@ static int __cfs_schedulable(struct task_group *tg, u64 period, u64 quota) return ret; } -static int cpu_stats_show(struct cgroup *cgrp, struct cftype *cft, +static int cpu_stats_show(struct cgroup_subsys_state *css, struct cftype *cft, struct cgroup_map_cb *cb) { - struct task_group *tg = cgroup_tg(cgrp); + struct task_group *tg = css_tg(css); struct cfs_bandwidth *cfs_b = &tg->cfs_bandwidth; cb->fill(cb, "nr_periods", cfs_b->nr_periods); @@ -7425,26 +7422,28 @@ static int cpu_stats_show(struct cgroup *cgrp, struct cftype *cft, #endif /* CONFIG_FAIR_GROUP_SCHED */ #ifdef CONFIG_RT_GROUP_SCHED -static int cpu_rt_runtime_write(struct cgroup *cgrp, struct cftype *cft, - s64 val) +static int cpu_rt_runtime_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) { - return sched_group_set_rt_runtime(cgroup_tg(cgrp), val); + return sched_group_set_rt_runtime(css_tg(css), val); } -static s64 cpu_rt_runtime_read(struct cgroup *cgrp, struct cftype *cft) +static s64 cpu_rt_runtime_read(struct cgroup_subsys_state *css, + struct cftype *cft) { - return sched_group_rt_runtime(cgroup_tg(cgrp)); + return sched_group_rt_runtime(css_tg(css)); } -static int cpu_rt_period_write_uint(struct cgroup *cgrp, struct cftype *cftype, - u64 rt_period_us) +static int cpu_rt_period_write_uint(struct cgroup_subsys_state *css, + struct cftype *cftype, u64 rt_period_us) { - return sched_group_set_rt_period(cgroup_tg(cgrp), rt_period_us); + return sched_group_set_rt_period(css_tg(css), rt_period_us); } -static u64 cpu_rt_period_read_uint(struct cgroup *cgrp, struct cftype *cft) +static u64 cpu_rt_period_read_uint(struct cgroup_subsys_state *css, + struct cftype *cft) { - return sched_group_rt_period(cgroup_tg(cgrp)); + return sched_group_rt_period(css_tg(css)); } #endif /* CONFIG_RT_GROUP_SCHED */ diff --git a/kernel/sched/cpuacct.c b/kernel/sched/cpuacct.c index 1b784d9b3630..f64722ff0299 100644 --- a/kernel/sched/cpuacct.c +++ b/kernel/sched/cpuacct.c @@ -38,12 +38,6 @@ static inline struct cpuacct *css_ca(struct cgroup_subsys_state *css) return css ? container_of(css, struct cpuacct, css) : NULL; } -/* return cpu accounting group corresponding to this container */ -static inline struct cpuacct *cgroup_ca(struct cgroup *cgrp) -{ - return css_ca(cgroup_css(cgrp, cpuacct_subsys_id)); -} - /* return cpu accounting group to which this task belongs */ static inline struct cpuacct *task_ca(struct task_struct *tsk) { @@ -138,9 +132,9 @@ static void cpuacct_cpuusage_write(struct cpuacct *ca, int cpu, u64 val) } /* return total cpu usage (in nanoseconds) of a group */ -static u64 cpuusage_read(struct cgroup *cgrp, struct cftype *cft) +static u64 cpuusage_read(struct cgroup_subsys_state *css, struct cftype *cft) { - struct cpuacct *ca = cgroup_ca(cgrp); + struct cpuacct *ca = css_ca(css); u64 totalcpuusage = 0; int i; @@ -150,10 +144,10 @@ static u64 cpuusage_read(struct cgroup *cgrp, struct cftype *cft) return totalcpuusage; } -static int cpuusage_write(struct cgroup *cgrp, struct cftype *cftype, - u64 reset) +static int cpuusage_write(struct cgroup_subsys_state *css, struct cftype *cft, + u64 reset) { - struct cpuacct *ca = cgroup_ca(cgrp); + struct cpuacct *ca = css_ca(css); int err = 0; int i; @@ -169,10 +163,10 @@ out: return err; } -static int cpuacct_percpu_seq_read(struct cgroup *cgroup, struct cftype *cft, - struct seq_file *m) +static int cpuacct_percpu_seq_read(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *m) { - struct cpuacct *ca = cgroup_ca(cgroup); + struct cpuacct *ca = css_ca(css); u64 percpu; int i; @@ -189,10 +183,10 @@ static const char * const cpuacct_stat_desc[] = { [CPUACCT_STAT_SYSTEM] = "system", }; -static int cpuacct_stats_show(struct cgroup *cgrp, struct cftype *cft, - struct cgroup_map_cb *cb) +static int cpuacct_stats_show(struct cgroup_subsys_state *css, + struct cftype *cft, struct cgroup_map_cb *cb) { - struct cpuacct *ca = cgroup_ca(cgrp); + struct cpuacct *ca = css_ca(css); int cpu; s64 val = 0; diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c index e2132435060f..bda8e44f6fde 100644 --- a/mm/hugetlb_cgroup.c +++ b/mm/hugetlb_cgroup.c @@ -39,12 +39,6 @@ struct hugetlb_cgroup *hugetlb_cgroup_from_css(struct cgroup_subsys_state *s) return s ? container_of(s, struct hugetlb_cgroup, css) : NULL; } -static inline -struct hugetlb_cgroup *hugetlb_cgroup_from_cgroup(struct cgroup *cgroup) -{ - return hugetlb_cgroup_from_css(cgroup_css(cgroup, hugetlb_subsys_id)); -} - static inline struct hugetlb_cgroup *hugetlb_cgroup_from_task(struct task_struct *task) { @@ -248,14 +242,15 @@ void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, return; } -static ssize_t hugetlb_cgroup_read(struct cgroup *cgroup, struct cftype *cft, - struct file *file, char __user *buf, - size_t nbytes, loff_t *ppos) +static ssize_t hugetlb_cgroup_read(struct cgroup_subsys_state *css, + struct cftype *cft, struct file *file, + char __user *buf, size_t nbytes, + loff_t *ppos) { u64 val; char str[64]; int idx, name, len; - struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup); + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(css); idx = MEMFILE_IDX(cft->private); name = MEMFILE_ATTR(cft->private); @@ -265,12 +260,12 @@ static ssize_t hugetlb_cgroup_read(struct cgroup *cgroup, struct cftype *cft, return simple_read_from_buffer(buf, nbytes, ppos, str, len); } -static int hugetlb_cgroup_write(struct cgroup *cgroup, struct cftype *cft, - const char *buffer) +static int hugetlb_cgroup_write(struct cgroup_subsys_state *css, + struct cftype *cft, const char *buffer) { int idx, name, ret; unsigned long long val; - struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup); + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(css); idx = MEMFILE_IDX(cft->private); name = MEMFILE_ATTR(cft->private); @@ -295,10 +290,11 @@ static int hugetlb_cgroup_write(struct cgroup *cgroup, struct cftype *cft, return ret; } -static int hugetlb_cgroup_reset(struct cgroup *cgroup, unsigned int event) +static int hugetlb_cgroup_reset(struct cgroup_subsys_state *css, + unsigned int event) { int idx, name, ret = 0; - struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup); + struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(css); idx = MEMFILE_IDX(event); name = MEMFILE_ATTR(event); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 32cca0f0af0d..ab64dfc84f8c 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -483,7 +483,6 @@ enum res_type { */ static DEFINE_MUTEX(memcg_create_mutex); -static inline struct mem_cgroup *mem_cgroup_from_css(struct cgroup_subsys_state *s) { return s ? container_of(s, struct mem_cgroup, css) : NULL; @@ -1035,7 +1034,7 @@ static void memcg_check_events(struct mem_cgroup *memcg, struct page *page) preempt_enable(); } -struct mem_cgroup *mem_cgroup_from_cont(struct cgroup *cont) +static inline struct mem_cgroup *mem_cgroup_from_cont(struct cgroup *cont) { return mem_cgroup_from_css(cgroup_css(cont, mem_cgroup_subsys_id)); } @@ -2951,10 +2950,10 @@ static struct kmem_cache *memcg_params_to_cache(struct memcg_cache_params *p) } #ifdef CONFIG_SLABINFO -static int mem_cgroup_slabinfo_read(struct cgroup *cont, struct cftype *cft, - struct seq_file *m) +static int mem_cgroup_slabinfo_read(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *m) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); struct memcg_cache_params *params; if (!memcg_can_account_kmem(memcg)) @@ -4999,9 +4998,10 @@ static int mem_cgroup_force_empty(struct mem_cgroup *memcg) return 0; } -static int mem_cgroup_force_empty_write(struct cgroup *cont, unsigned int event) +static int mem_cgroup_force_empty_write(struct cgroup_subsys_state *css, + unsigned int event) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); int ret; if (mem_cgroup_is_root(memcg)) @@ -5014,16 +5014,17 @@ static int mem_cgroup_force_empty_write(struct cgroup *cont, unsigned int event) } -static u64 mem_cgroup_hierarchy_read(struct cgroup *cont, struct cftype *cft) +static u64 mem_cgroup_hierarchy_read(struct cgroup_subsys_state *css, + struct cftype *cft) { - return mem_cgroup_from_cont(cont)->use_hierarchy; + return mem_cgroup_from_css(css)->use_hierarchy; } -static int mem_cgroup_hierarchy_write(struct cgroup *cont, struct cftype *cft, - u64 val) +static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css, + struct cftype *cft, u64 val) { int retval = 0; - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); struct mem_cgroup *parent_memcg = mem_cgroup_from_css(css_parent(&memcg->css)); mutex_lock(&memcg_create_mutex); @@ -5094,11 +5095,11 @@ static inline u64 mem_cgroup_usage(struct mem_cgroup *memcg, bool swap) return val << PAGE_SHIFT; } -static ssize_t mem_cgroup_read(struct cgroup *cont, struct cftype *cft, - struct file *file, char __user *buf, - size_t nbytes, loff_t *ppos) +static ssize_t mem_cgroup_read(struct cgroup_subsys_state *css, + struct cftype *cft, struct file *file, + char __user *buf, size_t nbytes, loff_t *ppos) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); char str[64]; u64 val; int name, len; @@ -5131,11 +5132,11 @@ static ssize_t mem_cgroup_read(struct cgroup *cont, struct cftype *cft, return simple_read_from_buffer(buf, nbytes, ppos, str, len); } -static int memcg_update_kmem_limit(struct cgroup *cont, u64 val) +static int memcg_update_kmem_limit(struct cgroup_subsys_state *css, u64 val) { int ret = -EINVAL; #ifdef CONFIG_MEMCG_KMEM - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); /* * For simplicity, we won't allow this to be disabled. It also can't * be changed if the cgroup has children already, or if tasks had @@ -5151,7 +5152,7 @@ static int memcg_update_kmem_limit(struct cgroup *cont, u64 val) mutex_lock(&memcg_create_mutex); mutex_lock(&set_limit_mutex); if (!memcg->kmem_account_flags && val != RESOURCE_MAX) { - if (cgroup_task_count(cont) || memcg_has_children(memcg)) { + if (cgroup_task_count(css->cgroup) || memcg_has_children(memcg)) { ret = -EBUSY; goto out; } @@ -5221,10 +5222,10 @@ out: * The user of this function is... * RES_LIMIT. */ -static int mem_cgroup_write(struct cgroup *cont, struct cftype *cft, +static int mem_cgroup_write(struct cgroup_subsys_state *css, struct cftype *cft, const char *buffer) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); enum res_type type; int name; unsigned long long val; @@ -5248,7 +5249,7 @@ static int mem_cgroup_write(struct cgroup *cont, struct cftype *cft, else if (type == _MEMSWAP) ret = mem_cgroup_resize_memsw_limit(memcg, val); else if (type == _KMEM) - ret = memcg_update_kmem_limit(cont, val); + ret = memcg_update_kmem_limit(css, val); else return -EINVAL; break; @@ -5297,9 +5298,9 @@ out: *memsw_limit = min_memsw_limit; } -static int mem_cgroup_reset(struct cgroup *cont, unsigned int event) +static int mem_cgroup_reset(struct cgroup_subsys_state *css, unsigned int event) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); int name; enum res_type type; @@ -5332,17 +5333,17 @@ static int mem_cgroup_reset(struct cgroup *cont, unsigned int event) return 0; } -static u64 mem_cgroup_move_charge_read(struct cgroup *cgrp, +static u64 mem_cgroup_move_charge_read(struct cgroup_subsys_state *css, struct cftype *cft) { - return mem_cgroup_from_cont(cgrp)->move_charge_at_immigrate; + return mem_cgroup_from_css(css)->move_charge_at_immigrate; } #ifdef CONFIG_MMU -static int mem_cgroup_move_charge_write(struct cgroup *cgrp, +static int mem_cgroup_move_charge_write(struct cgroup_subsys_state *css, struct cftype *cft, u64 val) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); if (val >= (1 << NR_MOVE_TYPE)) return -EINVAL; @@ -5357,7 +5358,7 @@ static int mem_cgroup_move_charge_write(struct cgroup *cgrp, return 0; } #else -static int mem_cgroup_move_charge_write(struct cgroup *cgrp, +static int mem_cgroup_move_charge_write(struct cgroup_subsys_state *css, struct cftype *cft, u64 val) { return -ENOSYS; @@ -5365,13 +5366,13 @@ static int mem_cgroup_move_charge_write(struct cgroup *cgrp, #endif #ifdef CONFIG_NUMA -static int memcg_numa_stat_show(struct cgroup *cont, struct cftype *cft, - struct seq_file *m) +static int memcg_numa_stat_show(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *m) { int nid; unsigned long total_nr, file_nr, anon_nr, unevictable_nr; unsigned long node_nr; - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); total_nr = mem_cgroup_nr_lru_pages(memcg, LRU_ALL); seq_printf(m, "total=%lu", total_nr); @@ -5416,10 +5417,10 @@ static inline void mem_cgroup_lru_names_not_uptodate(void) BUILD_BUG_ON(ARRAY_SIZE(mem_cgroup_lru_names) != NR_LRU_LISTS); } -static int memcg_stat_show(struct cgroup *cont, struct cftype *cft, +static int memcg_stat_show(struct cgroup_subsys_state *css, struct cftype *cft, struct seq_file *m) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); struct mem_cgroup *mi; unsigned int i; @@ -5503,17 +5504,18 @@ static int memcg_stat_show(struct cgroup *cont, struct cftype *cft, return 0; } -static u64 mem_cgroup_swappiness_read(struct cgroup *cgrp, struct cftype *cft) +static u64 mem_cgroup_swappiness_read(struct cgroup_subsys_state *css, + struct cftype *cft) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); return mem_cgroup_swappiness(memcg); } -static int mem_cgroup_swappiness_write(struct cgroup *cgrp, struct cftype *cft, - u64 val) +static int mem_cgroup_swappiness_write(struct cgroup_subsys_state *css, + struct cftype *cft, u64 val) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); struct mem_cgroup *parent = mem_cgroup_from_css(css_parent(&memcg->css)); if (val > 100 || !parent) @@ -5829,10 +5831,10 @@ static void mem_cgroup_oom_unregister_event(struct cgroup *cgrp, spin_unlock(&memcg_oom_lock); } -static int mem_cgroup_oom_control_read(struct cgroup *cgrp, +static int mem_cgroup_oom_control_read(struct cgroup_subsys_state *css, struct cftype *cft, struct cgroup_map_cb *cb) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); cb->fill(cb, "oom_kill_disable", memcg->oom_kill_disable); @@ -5843,10 +5845,10 @@ static int mem_cgroup_oom_control_read(struct cgroup *cgrp, return 0; } -static int mem_cgroup_oom_control_write(struct cgroup *cgrp, +static int mem_cgroup_oom_control_write(struct cgroup_subsys_state *css, struct cftype *cft, u64 val) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); struct mem_cgroup *parent = mem_cgroup_from_css(css_parent(&memcg->css)); /* cannot set to root cgroup and only 0 and 1 are allowed */ diff --git a/mm/vmpressure.c b/mm/vmpressure.c index 7f1654d3cec7..2a8a736e95cc 100644 --- a/mm/vmpressure.c +++ b/mm/vmpressure.c @@ -81,8 +81,8 @@ static struct vmpressure *cg_to_vmpressure(struct cgroup *cg) static struct vmpressure *vmpressure_parent(struct vmpressure *vmpr) { - struct cgroup *cg = vmpressure_to_css(vmpr)->cgroup; - struct mem_cgroup *memcg = mem_cgroup_from_cont(cg); + struct cgroup_subsys_state *css = vmpressure_to_css(vmpr); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); memcg = parent_mem_cgroup(memcg); if (!memcg) diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index 8d095b4c2f6f..e00f60e5baea 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -168,15 +168,14 @@ static void cgrp_css_free(struct cgroup_subsys_state *css) kfree(css); } -static u64 read_prioidx(struct cgroup *cgrp, struct cftype *cft) +static u64 read_prioidx(struct cgroup_subsys_state *css, struct cftype *cft) { - return cgrp->id; + return css->cgroup->id; } -static int read_priomap(struct cgroup *cont, struct cftype *cft, +static int read_priomap(struct cgroup_subsys_state *css, struct cftype *cft, struct cgroup_map_cb *cb) { - struct cgroup_subsys_state *css = cgroup_css(cont, net_prio_subsys_id); struct net_device *dev; rcu_read_lock(); @@ -186,10 +185,9 @@ static int read_priomap(struct cgroup *cont, struct cftype *cft, return 0; } -static int write_priomap(struct cgroup *cgrp, struct cftype *cft, +static int write_priomap(struct cgroup_subsys_state *css, struct cftype *cft, const char *buffer) { - struct cgroup_subsys_state *css = cgroup_css(cgrp, net_prio_subsys_id); char devname[IFNAMSIZ + 1]; struct net_device *dev; u32 prio; diff --git a/net/ipv4/tcp_memcontrol.c b/net/ipv4/tcp_memcontrol.c index da14436c1735..8a57d79b0b16 100644 --- a/net/ipv4/tcp_memcontrol.c +++ b/net/ipv4/tcp_memcontrol.c @@ -132,10 +132,10 @@ static int tcp_update_limit(struct mem_cgroup *memcg, u64 val) return 0; } -static int tcp_cgroup_write(struct cgroup *cont, struct cftype *cft, +static int tcp_cgroup_write(struct cgroup_subsys_state *css, struct cftype *cft, const char *buffer) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); unsigned long long val; int ret = 0; @@ -180,9 +180,9 @@ static u64 tcp_read_usage(struct mem_cgroup *memcg) return res_counter_read_u64(&tcp->tcp_memory_allocated, RES_USAGE); } -static u64 tcp_cgroup_read(struct cgroup *cont, struct cftype *cft) +static u64 tcp_cgroup_read(struct cgroup_subsys_state *css, struct cftype *cft) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); u64 val; switch (cft->private) { @@ -202,13 +202,13 @@ static u64 tcp_cgroup_read(struct cgroup *cont, struct cftype *cft) return val; } -static int tcp_cgroup_reset(struct cgroup *cont, unsigned int event) +static int tcp_cgroup_reset(struct cgroup_subsys_state *css, unsigned int event) { struct mem_cgroup *memcg; struct tcp_memcontrol *tcp; struct cg_proto *cg_proto; - memcg = mem_cgroup_from_cont(cont); + memcg = mem_cgroup_from_css(css); cg_proto = tcp_prot.proto_cgroup(memcg); if (!cg_proto) return 0; diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index dc3983835893..8ea1184cec92 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -28,11 +28,6 @@ static inline struct cgroup_cls_state *css_cls_state(struct cgroup_subsys_state return css ? container_of(css, struct cgroup_cls_state, css) : NULL; } -static inline struct cgroup_cls_state *cgrp_cls_state(struct cgroup *cgrp) -{ - return css_cls_state(cgroup_css(cgrp, net_cls_subsys_id)); -} - static inline struct cgroup_cls_state *task_cls_state(struct task_struct *p) { return css_cls_state(task_css(p, net_cls_subsys_id)); @@ -87,14 +82,15 @@ static void cgrp_attach(struct cgroup_subsys_state *css, } } -static u64 read_classid(struct cgroup *cgrp, struct cftype *cft) +static u64 read_classid(struct cgroup_subsys_state *css, struct cftype *cft) { - return cgrp_cls_state(cgrp)->classid; + return css_cls_state(css)->classid; } -static int write_classid(struct cgroup *cgrp, struct cftype *cft, u64 value) +static int write_classid(struct cgroup_subsys_state *css, struct cftype *cft, + u64 value) { - cgrp_cls_state(cgrp)->classid = (u32) value; + css_cls_state(css)->classid = (u32) value; return 0; } diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 7293ac49ba7b..e0ca464fa854 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -289,10 +289,10 @@ static void set_majmin(char *str, unsigned m) sprintf(str, "%u", m); } -static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft, - struct seq_file *m) +static int devcgroup_seq_read(struct cgroup_subsys_state *css, + struct cftype *cft, struct seq_file *m) { - struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup); + struct dev_cgroup *devcgroup = css_to_devcgroup(css); struct dev_exception_item *ex; char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN]; @@ -669,13 +669,13 @@ static int devcgroup_update_access(struct dev_cgroup *devcgroup, return rc; } -static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft, - const char *buffer) +static int devcgroup_access_write(struct cgroup_subsys_state *css, + struct cftype *cft, const char *buffer) { int retval; mutex_lock(&devcgroup_mutex); - retval = devcgroup_update_access(cgroup_to_devcgroup(cgrp), + retval = devcgroup_update_access(css_to_devcgroup(css), cft->private, buffer); mutex_unlock(&devcgroup_mutex); return retval; -- cgit v1.2.3-71-gd317 From 3b287a505ef4024634beb12a93773254909d5dae Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:24 -0400 Subject: cgroup: convert cgroup_next_sibling() to cgroup_next_child() cgroup is transitioning to using css (cgroup_subsys_state) as the main subsys interface handle instead of cgroup and the iterators will be updated to use css too. The iterators need to walk the cgroup hierarchy and return the css's matching the origin css, which is a bit cumbersome to open code. This patch converts cgroup_next_sibling() to cgroup_next_child() so that it can handle all steps of direct child iteration. This will be used to update iterators to take @css instead of @cgrp. In addition to the new iteration init handling, cgroup_next_child() is restructured so that the different branches share the end of iteration condition check. This patch doesn't change any behavior. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 4 ++-- kernel/cgroup.c | 59 +++++++++++++++++++++++++------------------------- 2 files changed, 32 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 0b91436c68ef..5f9ba5881717 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -779,7 +779,7 @@ static inline struct cgroup *cgroup_from_id(struct cgroup_subsys *ss, int id) return idr_find(&ss->root->cgroup_idr, id); } -struct cgroup *cgroup_next_sibling(struct cgroup *pos); +struct cgroup *cgroup_next_child(struct cgroup *pos, struct cgroup *cgrp); /** * cgroup_for_each_child - iterate through children of a cgroup @@ -802,7 +802,7 @@ struct cgroup *cgroup_next_sibling(struct cgroup *pos); #define cgroup_for_each_child(pos, cgrp) \ for ((pos) = list_first_or_null_rcu(&(cgrp)->children, \ struct cgroup, sibling); \ - (pos); (pos) = cgroup_next_sibling((pos))) + (pos); (pos) = cgroup_next_child((pos), (cgrp))) struct cgroup *cgroup_next_descendant_pre(struct cgroup *pos, struct cgroup *cgroup); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 6ee469837fda..dd55244952bd 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -3037,15 +3037,16 @@ static void cgroup_enable_task_cg_lists(void) } /** - * cgroup_next_sibling - find the next sibling of a given cgroup - * @pos: the current cgroup + * cgroup_next_child - find the next child of a given cgroup + * @pos: the current position (%NULL to initiate traversal) + * @cgrp: cgroup whose descendants to walk * - * This function returns the next sibling of @pos and should be called - * under RCU read lock. The only requirement is that @pos is accessible. - * The next sibling is guaranteed to be returned regardless of @pos's - * state. + * This function returns the next child of @cgrp and should be called under + * RCU read lock. The only requirement is that @cgrp and @pos are + * accessible. The next sibling is guaranteed to be returned regardless of + * their states. */ -struct cgroup *cgroup_next_sibling(struct cgroup *pos) +struct cgroup *cgroup_next_child(struct cgroup *pos, struct cgroup *cgrp) { struct cgroup *next; @@ -3061,30 +3062,30 @@ struct cgroup *cgroup_next_sibling(struct cgroup *pos) * safe to dereference from this RCU critical section. If * ->sibling.next is inaccessible, cgroup_is_dead() is guaranteed * to be visible as %true here. + * + * If @pos is dead, its next pointer can't be dereferenced; + * however, as each cgroup is given a monotonically increasing + * unique serial number and always appended to the sibling list, + * the next one can be found by walking the parent's children until + * we see a cgroup with higher serial number than @pos's. While + * this path can be slower, it's taken only when either the current + * cgroup is removed or iteration and removal race. */ - if (likely(!cgroup_is_dead(pos))) { + if (!pos) { + next = list_entry_rcu(cgrp->children.next, struct cgroup, sibling); + } else if (likely(!cgroup_is_dead(pos))) { next = list_entry_rcu(pos->sibling.next, struct cgroup, sibling); - if (&next->sibling != &pos->parent->children) - return next; - return NULL; + } else { + list_for_each_entry_rcu(next, &cgrp->children, sibling) + if (next->serial_nr > pos->serial_nr) + break; } - /* - * Can't dereference the next pointer. Each cgroup is given a - * monotonically increasing unique serial number and always - * appended to the sibling list, so the next one can be found by - * walking the parent's children until we see a cgroup with higher - * serial number than @pos's. - * - * While this path can be slow, it's taken only when either the - * current cgroup is removed or iteration and removal race. - */ - list_for_each_entry_rcu(next, &pos->parent->children, sibling) - if (next->serial_nr > pos->serial_nr) - return next; + if (&next->sibling != &cgrp->children) + return next; return NULL; } -EXPORT_SYMBOL_GPL(cgroup_next_sibling); +EXPORT_SYMBOL_GPL(cgroup_next_child); /** * cgroup_next_descendant_pre - find the next descendant for pre-order walk @@ -3117,7 +3118,7 @@ struct cgroup *cgroup_next_descendant_pre(struct cgroup *pos, /* no child, visit my or the closest ancestor's next sibling */ while (pos != cgroup) { - next = cgroup_next_sibling(pos); + next = cgroup_next_child(pos, pos->parent); if (next) return next; pos = pos->parent; @@ -3198,7 +3199,7 @@ struct cgroup *cgroup_next_descendant_post(struct cgroup *pos, } /* if there's an unvisited sibling, visit its leftmost descendant */ - next = cgroup_next_sibling(pos); + next = cgroup_next_child(pos, pos->parent); if (next) return cgroup_leftmost_descendant(next); @@ -4549,9 +4550,9 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) /* * Mark @cgrp dead. This prevents further task migration and child * creation by disabling cgroup_lock_live_group(). Note that - * CGRP_DEAD assertion is depended upon by cgroup_next_sibling() to + * CGRP_DEAD assertion is depended upon by cgroup_next_child() to * resume iteration after dropping RCU read lock. See - * cgroup_next_sibling() for details. + * cgroup_next_child() for details. */ set_bit(CGRP_DEAD, &cgrp->flags); -- cgit v1.2.3-71-gd317 From f48e3924dca268c677c4e338e5d91ad9e6fe6b9e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:24 -0400 Subject: cgroup: always use cgroup_next_child() to walk the children list There are several places where the children list is accessed directly. This patch converts those places to use cgroup_next_child(). This will help updating the hierarchy iterators to use @css instead of @cgrp. While cgroup_next_child() can be heavy in pathological cases - e.g. a lot of dead children, this shouldn't cause any noticeable behavior differences. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 5 ++--- kernel/cgroup.c | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 5f9ba5881717..c288bce428f8 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -800,9 +800,8 @@ struct cgroup *cgroup_next_child(struct cgroup *pos, struct cgroup *cgrp); * the start of the next iteration by, for example, bumping the css refcnt. */ #define cgroup_for_each_child(pos, cgrp) \ - for ((pos) = list_first_or_null_rcu(&(cgrp)->children, \ - struct cgroup, sibling); \ - (pos); (pos) = cgroup_next_child((pos), (cgrp))) + for ((pos) = cgroup_next_child(NULL, (cgrp)); (pos); \ + (pos) = cgroup_next_child((pos), (cgrp))) struct cgroup *cgroup_next_descendant_pre(struct cgroup *pos, struct cgroup *cgroup); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index dd55244952bd..2b7354faaca7 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -3112,7 +3112,7 @@ struct cgroup *cgroup_next_descendant_pre(struct cgroup *pos, pos = cgroup; /* visit the first child if exists */ - next = list_first_or_null_rcu(&pos->children, struct cgroup, sibling); + next = cgroup_next_child(NULL, pos); if (next) return next; @@ -3151,7 +3151,7 @@ struct cgroup *cgroup_rightmost_descendant(struct cgroup *pos) last = pos; /* ->prev isn't RCU safe, walk ->next till the end */ pos = NULL; - list_for_each_entry_rcu(tmp, &last->children, sibling) + cgroup_for_each_child(tmp, last) pos = tmp; } while (pos); @@ -3165,8 +3165,7 @@ static struct cgroup *cgroup_leftmost_descendant(struct cgroup *pos) do { last = pos; - pos = list_first_or_null_rcu(&pos->children, struct cgroup, - sibling); + pos = cgroup_next_child(NULL, pos); } while (pos); return last; -- cgit v1.2.3-71-gd317 From 492eb21b98f88e411a8bb43d6edcd7d7022add10 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:25 -0400 Subject: cgroup: make hierarchy iterators deal with cgroup_subsys_state instead of cgroup cgroup is currently in the process of transitioning to using css (cgroup_subsys_state) as the primary handle instead of cgroup in subsystem API. For hierarchy iterators, this is beneficial because * In most cases, css is the only thing subsystems care about anyway. * On the planned unified hierarchy, iterations for different subsystems will need to skip over different subtrees of the hierarchy depending on which subsystems are enabled on each cgroup. Passing around css makes it unnecessary to explicitly specify the subsystem in question as css is intersection between cgroup and subsystem * For the planned unified hierarchy, css's would need to be created and destroyed dynamically independent from cgroup hierarchy. Having cgroup core manage css iteration makes enforcing deref rules a lot easier. Most subsystem conversions are straight-forward. Noteworthy changes are * blkio: cgroup_to_blkcg() is no longer used. Removed. * freezer: cgroup_freezer() is no longer used. Removed. * devices: cgroup_to_devcgroup() is no longer used. Removed. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Michal Hocko Acked-by: Vivek Goyal Acked-by: Aristeu Rozanski Cc: Johannes Weiner Cc: Balbir Singh Cc: Matt Helsley Cc: Jens Axboe --- block/blk-cgroup.c | 8 +-- block/blk-cgroup.h | 25 ++++----- block/blk-throttle.c | 8 +-- include/linux/cgroup.h | 88 ++++++++++++++++--------------- kernel/cgroup.c | 131 ++++++++++++++++++++++++++--------------------- kernel/cgroup_freezer.c | 25 ++++----- kernel/cpuset.c | 58 ++++++++++----------- mm/memcontrol.c | 20 ++++---- security/device_cgroup.c | 11 ++-- 9 files changed, 187 insertions(+), 187 deletions(-) (limited to 'include') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index f46f3c69179c..4b40640240a4 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -614,7 +614,7 @@ u64 blkg_stat_recursive_sum(struct blkg_policy_data *pd, int off) { struct blkcg_policy *pol = blkcg_policy[pd->plid]; struct blkcg_gq *pos_blkg; - struct cgroup *pos_cgrp; + struct cgroup_subsys_state *pos_css; u64 sum; lockdep_assert_held(pd->blkg->q->queue_lock); @@ -622,7 +622,7 @@ u64 blkg_stat_recursive_sum(struct blkg_policy_data *pd, int off) sum = blkg_stat_read((void *)pd + off); rcu_read_lock(); - blkg_for_each_descendant_pre(pos_blkg, pos_cgrp, pd_to_blkg(pd)) { + blkg_for_each_descendant_pre(pos_blkg, pos_css, pd_to_blkg(pd)) { struct blkg_policy_data *pos_pd = blkg_to_pd(pos_blkg, pol); struct blkg_stat *stat = (void *)pos_pd + off; @@ -649,7 +649,7 @@ struct blkg_rwstat blkg_rwstat_recursive_sum(struct blkg_policy_data *pd, { struct blkcg_policy *pol = blkcg_policy[pd->plid]; struct blkcg_gq *pos_blkg; - struct cgroup *pos_cgrp; + struct cgroup_subsys_state *pos_css; struct blkg_rwstat sum; int i; @@ -658,7 +658,7 @@ struct blkg_rwstat blkg_rwstat_recursive_sum(struct blkg_policy_data *pd, sum = blkg_rwstat_read((void *)pd + off); rcu_read_lock(); - blkg_for_each_descendant_pre(pos_blkg, pos_cgrp, pd_to_blkg(pd)) { + blkg_for_each_descendant_pre(pos_blkg, pos_css, pd_to_blkg(pd)) { struct blkg_policy_data *pos_pd = blkg_to_pd(pos_blkg, pol); struct blkg_rwstat *rwstat = (void *)pos_pd + off; struct blkg_rwstat tmp; diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index b6802c46d68f..855538630300 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -184,11 +184,6 @@ static inline struct blkcg *css_to_blkcg(struct cgroup_subsys_state *css) return css ? container_of(css, struct blkcg, css) : NULL; } -static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) -{ - return css_to_blkcg(cgroup_css(cgroup, blkio_subsys_id)); -} - static inline struct blkcg *task_blkcg(struct task_struct *tsk) { return css_to_blkcg(task_css(tsk, blkio_subsys_id)); @@ -289,32 +284,31 @@ struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q, /** * blkg_for_each_descendant_pre - pre-order walk of a blkg's descendants * @d_blkg: loop cursor pointing to the current descendant - * @pos_cgrp: used for iteration + * @pos_css: used for iteration * @p_blkg: target blkg to walk descendants of * * Walk @c_blkg through the descendants of @p_blkg. Must be used with RCU * read locked. If called under either blkcg or queue lock, the iteration * is guaranteed to include all and only online blkgs. The caller may - * update @pos_cgrp by calling cgroup_rightmost_descendant() to skip - * subtree. + * update @pos_css by calling css_rightmost_descendant() to skip subtree. */ -#define blkg_for_each_descendant_pre(d_blkg, pos_cgrp, p_blkg) \ - cgroup_for_each_descendant_pre((pos_cgrp), (p_blkg)->blkcg->css.cgroup) \ - if (((d_blkg) = __blkg_lookup(cgroup_to_blkcg(pos_cgrp), \ +#define blkg_for_each_descendant_pre(d_blkg, pos_css, p_blkg) \ + css_for_each_descendant_pre((pos_css), &(p_blkg)->blkcg->css) \ + if (((d_blkg) = __blkg_lookup(css_to_blkcg(pos_css), \ (p_blkg)->q, false))) /** * blkg_for_each_descendant_post - post-order walk of a blkg's descendants * @d_blkg: loop cursor pointing to the current descendant - * @pos_cgrp: used for iteration + * @pos_css: used for iteration * @p_blkg: target blkg to walk descendants of * * Similar to blkg_for_each_descendant_pre() but performs post-order * traversal instead. Synchronization rules are the same. */ -#define blkg_for_each_descendant_post(d_blkg, pos_cgrp, p_blkg) \ - cgroup_for_each_descendant_post((pos_cgrp), (p_blkg)->blkcg->css.cgroup) \ - if (((d_blkg) = __blkg_lookup(cgroup_to_blkcg(pos_cgrp), \ +#define blkg_for_each_descendant_post(d_blkg, pos_css, p_blkg) \ + css_for_each_descendant_post((pos_css), &(p_blkg)->blkcg->css) \ + if (((d_blkg) = __blkg_lookup(css_to_blkcg(pos_css), \ (p_blkg)->q, false))) /** @@ -577,7 +571,6 @@ static inline int blkcg_activate_policy(struct request_queue *q, static inline void blkcg_deactivate_policy(struct request_queue *q, const struct blkcg_policy *pol) { } -static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) { return NULL; } static inline struct blkcg *bio_blkcg(struct bio *bio) { return NULL; } static inline struct blkg_policy_data *blkg_to_pd(struct blkcg_gq *blkg, diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 88bcfb651b0b..8cefa7f8590e 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -1349,7 +1349,7 @@ static int tg_set_conf(struct cgroup_subsys_state *css, struct cftype *cft, struct throtl_grp *tg; struct throtl_service_queue *sq; struct blkcg_gq *blkg; - struct cgroup *pos_cgrp; + struct cgroup_subsys_state *pos_css; int ret; ret = blkg_conf_prep(blkcg, &blkcg_policy_throtl, buf, &ctx); @@ -1380,7 +1380,7 @@ static int tg_set_conf(struct cgroup_subsys_state *css, struct cftype *cft, * blk-throttle. */ tg_update_has_rules(tg); - blkg_for_each_descendant_pre(blkg, pos_cgrp, ctx.blkg) + blkg_for_each_descendant_pre(blkg, pos_css, ctx.blkg) tg_update_has_rules(blkg_to_tg(blkg)); /* @@ -1623,7 +1623,7 @@ void blk_throtl_drain(struct request_queue *q) { struct throtl_data *td = q->td; struct blkcg_gq *blkg; - struct cgroup *pos_cgrp; + struct cgroup_subsys_state *pos_css; struct bio *bio; int rw; @@ -1636,7 +1636,7 @@ void blk_throtl_drain(struct request_queue *q) * better to walk service_queue tree directly but blkg walk is * easier. */ - blkg_for_each_descendant_post(blkg, pos_cgrp, td->queue->root_blkg) + blkg_for_each_descendant_post(blkg, pos_css, td->queue->root_blkg) tg_drain_bios(&blkg_to_tg(blkg)->service_queue); tg_drain_bios(&td_root_tg(td)->service_queue); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c288bce428f8..4bc22f4a1abb 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -779,68 +779,72 @@ static inline struct cgroup *cgroup_from_id(struct cgroup_subsys *ss, int id) return idr_find(&ss->root->cgroup_idr, id); } -struct cgroup *cgroup_next_child(struct cgroup *pos, struct cgroup *cgrp); +struct cgroup_subsys_state *css_next_child(struct cgroup_subsys_state *pos, + struct cgroup_subsys_state *parent); /** - * cgroup_for_each_child - iterate through children of a cgroup - * @pos: the cgroup * to use as the loop cursor - * @cgrp: cgroup whose children to walk + * css_for_each_child - iterate through children of a css + * @pos: the css * to use as the loop cursor + * @parent: css whose children to walk * - * Walk @cgrp's children. Must be called under rcu_read_lock(). A child - * cgroup which hasn't finished ->css_online() or already has finished + * Walk @parent's children. Must be called under rcu_read_lock(). A child + * css which hasn't finished ->css_online() or already has finished * ->css_offline() may show up during traversal and it's each subsystem's * responsibility to verify that each @pos is alive. * * If a subsystem synchronizes against the parent in its ->css_online() and - * before starting iterating, a cgroup which finished ->css_online() is + * before starting iterating, a css which finished ->css_online() is * guaranteed to be visible in the future iterations. * * It is allowed to temporarily drop RCU read lock during iteration. The * caller is responsible for ensuring that @pos remains accessible until * the start of the next iteration by, for example, bumping the css refcnt. */ -#define cgroup_for_each_child(pos, cgrp) \ - for ((pos) = cgroup_next_child(NULL, (cgrp)); (pos); \ - (pos) = cgroup_next_child((pos), (cgrp))) +#define css_for_each_child(pos, parent) \ + for ((pos) = css_next_child(NULL, (parent)); (pos); \ + (pos) = css_next_child((pos), (parent))) -struct cgroup *cgroup_next_descendant_pre(struct cgroup *pos, - struct cgroup *cgroup); -struct cgroup *cgroup_rightmost_descendant(struct cgroup *pos); +struct cgroup_subsys_state * +css_next_descendant_pre(struct cgroup_subsys_state *pos, + struct cgroup_subsys_state *css); + +struct cgroup_subsys_state * +css_rightmost_descendant(struct cgroup_subsys_state *pos); /** - * cgroup_for_each_descendant_pre - pre-order walk of a cgroup's descendants - * @pos: the cgroup * to use as the loop cursor - * @cgroup: cgroup whose descendants to walk + * css_for_each_descendant_pre - pre-order walk of a css's descendants + * @pos: the css * to use as the loop cursor + * @root: css whose descendants to walk * - * Walk @cgroup's descendants. Must be called under rcu_read_lock(). A - * descendant cgroup which hasn't finished ->css_online() or already has + * Walk @root's descendants. Must be called under rcu_read_lock(). A + * descendant css which hasn't finished ->css_online() or already has * finished ->css_offline() may show up during traversal and it's each * subsystem's responsibility to verify that each @pos is alive. * * If a subsystem synchronizes against the parent in its ->css_online() and * before starting iterating, and synchronizes against @pos on each - * iteration, any descendant cgroup which finished ->css_online() is + * iteration, any descendant css which finished ->css_online() is * guaranteed to be visible in the future iterations. * * In other words, the following guarantees that a descendant can't escape * state updates of its ancestors. * - * my_online(@cgrp) + * my_online(@css) * { - * Lock @cgrp->parent and @cgrp; - * Inherit state from @cgrp->parent; + * Lock @css's parent and @css; + * Inherit state from the parent; * Unlock both. * } * - * my_update_state(@cgrp) + * my_update_state(@css) * { - * Lock @cgrp; - * Update @cgrp's state; - * Unlock @cgrp; + * Lock @css; + * Update @css's state; + * Unlock @css; * - * cgroup_for_each_descendant_pre(@pos, @cgrp) { + * css_for_each_descendant_pre(@pos, @css) { * Lock @pos; - * Verify @pos is alive and inherit state from @pos->parent; + * Verify @pos is alive and inherit state from @pos's parent; * Unlock @pos; * } * } @@ -851,8 +855,7 @@ struct cgroup *cgroup_rightmost_descendant(struct cgroup *pos); * visible by walking order and, as long as inheriting operations to the * same @pos are atomic to each other, multiple updates racing each other * still result in the correct state. It's guaranateed that at least one - * inheritance happens for any cgroup after the latest update to its - * parent. + * inheritance happens for any css after the latest update to its parent. * * If checking parent's state requires locking the parent, each inheriting * iteration should lock and unlock both @pos->parent and @pos. @@ -865,25 +868,26 @@ struct cgroup *cgroup_rightmost_descendant(struct cgroup *pos); * caller is responsible for ensuring that @pos remains accessible until * the start of the next iteration by, for example, bumping the css refcnt. */ -#define cgroup_for_each_descendant_pre(pos, cgroup) \ - for (pos = cgroup_next_descendant_pre(NULL, (cgroup)); (pos); \ - pos = cgroup_next_descendant_pre((pos), (cgroup))) +#define css_for_each_descendant_pre(pos, css) \ + for ((pos) = css_next_descendant_pre(NULL, (css)); (pos); \ + (pos) = css_next_descendant_pre((pos), (css))) -struct cgroup *cgroup_next_descendant_post(struct cgroup *pos, - struct cgroup *cgroup); +struct cgroup_subsys_state * +css_next_descendant_post(struct cgroup_subsys_state *pos, + struct cgroup_subsys_state *css); /** - * cgroup_for_each_descendant_post - post-order walk of a cgroup's descendants - * @pos: the cgroup * to use as the loop cursor - * @cgroup: cgroup whose descendants to walk + * css_for_each_descendant_post - post-order walk of a css's descendants + * @pos: the css * to use as the loop cursor + * @css: css whose descendants to walk * - * Similar to cgroup_for_each_descendant_pre() but performs post-order + * Similar to css_for_each_descendant_pre() but performs post-order * traversal instead. Note that the walk visibility guarantee described in * pre-order walk doesn't apply the same to post-order walks. */ -#define cgroup_for_each_descendant_post(pos, cgroup) \ - for (pos = cgroup_next_descendant_post(NULL, (cgroup)); (pos); \ - pos = cgroup_next_descendant_post((pos), (cgroup))) +#define css_for_each_descendant_post(pos, css) \ + for ((pos) = css_next_descendant_post(NULL, (css)); (pos); \ + (pos) = css_next_descendant_post((pos), (css))) /* A cgroup_iter should be treated as an opaque object */ struct cgroup_iter { diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 2b7354faaca7..91eac33fac86 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2814,8 +2814,8 @@ static void cgroup_cfts_prepare(void) /* * Thanks to the entanglement with vfs inode locking, we can't walk * the existing cgroups under cgroup_mutex and create files. - * Instead, we use cgroup_for_each_descendant_pre() and drop RCU - * read lock before calling cgroup_addrm_files(). + * Instead, we use css_for_each_descendant_pre() and drop RCU read + * lock before calling cgroup_addrm_files(). */ mutex_lock(&cgroup_mutex); } @@ -2825,10 +2825,11 @@ static int cgroup_cfts_commit(struct cftype *cfts, bool is_add) { LIST_HEAD(pending); struct cgroup_subsys *ss = cfts[0].ss; - struct cgroup *cgrp, *root = &ss->root->top_cgroup; + struct cgroup *root = &ss->root->top_cgroup; struct super_block *sb = ss->root->sb; struct dentry *prev = NULL; struct inode *inode; + struct cgroup_subsys_state *css; u64 update_before; int ret = 0; @@ -2861,7 +2862,9 @@ static int cgroup_cfts_commit(struct cftype *cfts, bool is_add) /* add/rm files for all cgroups created before */ rcu_read_lock(); - cgroup_for_each_descendant_pre(cgrp, root) { + css_for_each_descendant_pre(css, cgroup_css(root, ss->subsys_id)) { + struct cgroup *cgrp = css->cgroup; + if (cgroup_is_dead(cgrp)) continue; @@ -3037,17 +3040,21 @@ static void cgroup_enable_task_cg_lists(void) } /** - * cgroup_next_child - find the next child of a given cgroup - * @pos: the current position (%NULL to initiate traversal) - * @cgrp: cgroup whose descendants to walk + * css_next_child - find the next child of a given css + * @pos_css: the current position (%NULL to initiate traversal) + * @parent_css: css whose children to walk * - * This function returns the next child of @cgrp and should be called under - * RCU read lock. The only requirement is that @cgrp and @pos are - * accessible. The next sibling is guaranteed to be returned regardless of - * their states. + * This function returns the next child of @parent_css and should be called + * under RCU read lock. The only requirement is that @parent_css and + * @pos_css are accessible. The next sibling is guaranteed to be returned + * regardless of their states. */ -struct cgroup *cgroup_next_child(struct cgroup *pos, struct cgroup *cgrp) +struct cgroup_subsys_state * +css_next_child(struct cgroup_subsys_state *pos_css, + struct cgroup_subsys_state *parent_css) { + struct cgroup *pos = pos_css ? pos_css->cgroup : NULL; + struct cgroup *cgrp = parent_css->cgroup; struct cgroup *next; WARN_ON_ONCE(!rcu_read_lock_held()); @@ -3081,59 +3088,64 @@ struct cgroup *cgroup_next_child(struct cgroup *pos, struct cgroup *cgrp) break; } - if (&next->sibling != &cgrp->children) - return next; - return NULL; + if (&next->sibling == &cgrp->children) + return NULL; + + if (parent_css->ss) + return cgroup_css(next, parent_css->ss->subsys_id); + else + return &next->dummy_css; } -EXPORT_SYMBOL_GPL(cgroup_next_child); +EXPORT_SYMBOL_GPL(css_next_child); /** - * cgroup_next_descendant_pre - find the next descendant for pre-order walk + * css_next_descendant_pre - find the next descendant for pre-order walk * @pos: the current position (%NULL to initiate traversal) - * @cgroup: cgroup whose descendants to walk + * @root: css whose descendants to walk * - * To be used by cgroup_for_each_descendant_pre(). Find the next - * descendant to visit for pre-order traversal of @cgroup's descendants. + * To be used by css_for_each_descendant_pre(). Find the next descendant + * to visit for pre-order traversal of @root's descendants. * * While this function requires RCU read locking, it doesn't require the * whole traversal to be contained in a single RCU critical section. This * function will return the correct next descendant as long as both @pos - * and @cgroup are accessible and @pos is a descendant of @cgroup. + * and @root are accessible and @pos is a descendant of @root. */ -struct cgroup *cgroup_next_descendant_pre(struct cgroup *pos, - struct cgroup *cgroup) +struct cgroup_subsys_state * +css_next_descendant_pre(struct cgroup_subsys_state *pos, + struct cgroup_subsys_state *root) { - struct cgroup *next; + struct cgroup_subsys_state *next; WARN_ON_ONCE(!rcu_read_lock_held()); - /* if first iteration, pretend we just visited @cgroup */ + /* if first iteration, pretend we just visited @root */ if (!pos) - pos = cgroup; + pos = root; /* visit the first child if exists */ - next = cgroup_next_child(NULL, pos); + next = css_next_child(NULL, pos); if (next) return next; /* no child, visit my or the closest ancestor's next sibling */ - while (pos != cgroup) { - next = cgroup_next_child(pos, pos->parent); + while (pos != root) { + next = css_next_child(pos, css_parent(pos)); if (next) return next; - pos = pos->parent; + pos = css_parent(pos); } return NULL; } -EXPORT_SYMBOL_GPL(cgroup_next_descendant_pre); +EXPORT_SYMBOL_GPL(css_next_descendant_pre); /** - * cgroup_rightmost_descendant - return the rightmost descendant of a cgroup - * @pos: cgroup of interest + * css_rightmost_descendant - return the rightmost descendant of a css + * @pos: css of interest * - * Return the rightmost descendant of @pos. If there's no descendant, - * @pos is returned. This can be used during pre-order traversal to skip + * Return the rightmost descendant of @pos. If there's no descendant, @pos + * is returned. This can be used during pre-order traversal to skip * subtree of @pos. * * While this function requires RCU read locking, it doesn't require the @@ -3141,9 +3153,10 @@ EXPORT_SYMBOL_GPL(cgroup_next_descendant_pre); * function will return the correct rightmost descendant as long as @pos is * accessible. */ -struct cgroup *cgroup_rightmost_descendant(struct cgroup *pos) +struct cgroup_subsys_state * +css_rightmost_descendant(struct cgroup_subsys_state *pos) { - struct cgroup *last, *tmp; + struct cgroup_subsys_state *last, *tmp; WARN_ON_ONCE(!rcu_read_lock_held()); @@ -3151,62 +3164,64 @@ struct cgroup *cgroup_rightmost_descendant(struct cgroup *pos) last = pos; /* ->prev isn't RCU safe, walk ->next till the end */ pos = NULL; - cgroup_for_each_child(tmp, last) + css_for_each_child(tmp, last) pos = tmp; } while (pos); return last; } -EXPORT_SYMBOL_GPL(cgroup_rightmost_descendant); +EXPORT_SYMBOL_GPL(css_rightmost_descendant); -static struct cgroup *cgroup_leftmost_descendant(struct cgroup *pos) +static struct cgroup_subsys_state * +css_leftmost_descendant(struct cgroup_subsys_state *pos) { - struct cgroup *last; + struct cgroup_subsys_state *last; do { last = pos; - pos = cgroup_next_child(NULL, pos); + pos = css_next_child(NULL, pos); } while (pos); return last; } /** - * cgroup_next_descendant_post - find the next descendant for post-order walk + * css_next_descendant_post - find the next descendant for post-order walk * @pos: the current position (%NULL to initiate traversal) - * @cgroup: cgroup whose descendants to walk + * @root: css whose descendants to walk * - * To be used by cgroup_for_each_descendant_post(). Find the next - * descendant to visit for post-order traversal of @cgroup's descendants. + * To be used by css_for_each_descendant_post(). Find the next descendant + * to visit for post-order traversal of @root's descendants. * * While this function requires RCU read locking, it doesn't require the * whole traversal to be contained in a single RCU critical section. This * function will return the correct next descendant as long as both @pos * and @cgroup are accessible and @pos is a descendant of @cgroup. */ -struct cgroup *cgroup_next_descendant_post(struct cgroup *pos, - struct cgroup *cgroup) +struct cgroup_subsys_state * +css_next_descendant_post(struct cgroup_subsys_state *pos, + struct cgroup_subsys_state *root) { - struct cgroup *next; + struct cgroup_subsys_state *next; WARN_ON_ONCE(!rcu_read_lock_held()); /* if first iteration, visit the leftmost descendant */ if (!pos) { - next = cgroup_leftmost_descendant(cgroup); - return next != cgroup ? next : NULL; + next = css_leftmost_descendant(root); + return next != root ? next : NULL; } /* if there's an unvisited sibling, visit its leftmost descendant */ - next = cgroup_next_child(pos, pos->parent); + next = css_next_child(pos, css_parent(pos)); if (next) - return cgroup_leftmost_descendant(next); + return css_leftmost_descendant(next); /* no sibling left, visit parent */ - next = pos->parent; - return next != cgroup ? next : NULL; + next = css_parent(pos); + return next != root ? next : NULL; } -EXPORT_SYMBOL_GPL(cgroup_next_descendant_post); +EXPORT_SYMBOL_GPL(css_next_descendant_post); void cgroup_iter_start(struct cgroup *cgrp, struct cgroup_iter *it) __acquires(css_set_lock) @@ -4549,9 +4564,9 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) /* * Mark @cgrp dead. This prevents further task migration and child * creation by disabling cgroup_lock_live_group(). Note that - * CGRP_DEAD assertion is depended upon by cgroup_next_child() to + * CGRP_DEAD assertion is depended upon by css_next_child() to * resume iteration after dropping RCU read lock. See - * cgroup_next_child() for details. + * css_next_child() for details. */ set_bit(CGRP_DEAD, &cgrp->flags); diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 19613ba51444..98ca48d9ceb4 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -50,11 +50,6 @@ static inline struct freezer *css_freezer(struct cgroup_subsys_state *css) return css ? container_of(css, struct freezer, css) : NULL; } -static inline struct freezer *cgroup_freezer(struct cgroup *cgroup) -{ - return css_freezer(cgroup_css(cgroup, freezer_subsys_id)); -} - static inline struct freezer *task_freezer(struct task_struct *task) { return css_freezer(task_css(task, freezer_subsys_id)); @@ -120,7 +115,7 @@ static int freezer_css_online(struct cgroup_subsys_state *css) /* * The following double locking and freezing state inheritance * guarantee that @cgroup can never escape ancestors' freezing - * states. See cgroup_for_each_descendant_pre() for details. + * states. See css_for_each_descendant_pre() for details. */ if (parent) spin_lock_irq(&parent->lock); @@ -262,7 +257,7 @@ out: static void update_if_frozen(struct cgroup_subsys_state *css) { struct freezer *freezer = css_freezer(css); - struct cgroup *pos; + struct cgroup_subsys_state *pos; struct cgroup_iter it; struct task_struct *task; @@ -275,8 +270,8 @@ static void update_if_frozen(struct cgroup_subsys_state *css) goto out_unlock; /* are all (live) children frozen? */ - cgroup_for_each_child(pos, css->cgroup) { - struct freezer *child = cgroup_freezer(pos); + css_for_each_child(pos, css) { + struct freezer *child = css_freezer(pos); if ((child->state & CGROUP_FREEZER_ONLINE) && !(child->state & CGROUP_FROZEN)) @@ -309,13 +304,13 @@ out_unlock: static int freezer_read(struct cgroup_subsys_state *css, struct cftype *cft, struct seq_file *m) { - struct cgroup *pos; + struct cgroup_subsys_state *pos; rcu_read_lock(); /* update states bottom-up */ - cgroup_for_each_descendant_post(pos, css->cgroup) - update_if_frozen(cgroup_css(pos, freezer_subsys_id)); + css_for_each_descendant_post(pos, css) + update_if_frozen(pos); update_if_frozen(css); rcu_read_unlock(); @@ -396,7 +391,7 @@ static void freezer_apply_state(struct freezer *freezer, bool freeze, */ static void freezer_change_state(struct freezer *freezer, bool freeze) { - struct cgroup *pos; + struct cgroup_subsys_state *pos; /* update @freezer */ spin_lock_irq(&freezer->lock); @@ -409,8 +404,8 @@ static void freezer_change_state(struct freezer *freezer, bool freeze) * CGROUP_FREEZING_PARENT. */ rcu_read_lock(); - cgroup_for_each_descendant_pre(pos, freezer->css.cgroup) { - struct freezer *pos_f = cgroup_freezer(pos); + css_for_each_descendant_pre(pos, &freezer->css) { + struct freezer *pos_f = css_freezer(pos); struct freezer *parent = parent_freezer(pos_f); /* diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 89b76e1d3aa1..be4f5036ea5e 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -210,29 +210,29 @@ static struct cpuset top_cpuset = { /** * cpuset_for_each_child - traverse online children of a cpuset * @child_cs: loop cursor pointing to the current child - * @pos_cgrp: used for iteration + * @pos_css: used for iteration * @parent_cs: target cpuset to walk children of * * Walk @child_cs through the online children of @parent_cs. Must be used * with RCU read locked. */ -#define cpuset_for_each_child(child_cs, pos_cgrp, parent_cs) \ - cgroup_for_each_child((pos_cgrp), (parent_cs)->css.cgroup) \ - if (is_cpuset_online(((child_cs) = cgroup_cs((pos_cgrp))))) +#define cpuset_for_each_child(child_cs, pos_css, parent_cs) \ + css_for_each_child((pos_css), &(parent_cs)->css) \ + if (is_cpuset_online(((child_cs) = css_cs((pos_css))))) /** * cpuset_for_each_descendant_pre - pre-order walk of a cpuset's descendants * @des_cs: loop cursor pointing to the current descendant - * @pos_cgrp: used for iteration + * @pos_css: used for iteration * @root_cs: target cpuset to walk ancestor of * * Walk @des_cs through the online descendants of @root_cs. Must be used - * with RCU read locked. The caller may modify @pos_cgrp by calling - * cgroup_rightmost_descendant() to skip subtree. + * with RCU read locked. The caller may modify @pos_css by calling + * css_rightmost_descendant() to skip subtree. */ -#define cpuset_for_each_descendant_pre(des_cs, pos_cgrp, root_cs) \ - cgroup_for_each_descendant_pre((pos_cgrp), (root_cs)->css.cgroup) \ - if (is_cpuset_online(((des_cs) = cgroup_cs((pos_cgrp))))) +#define cpuset_for_each_descendant_pre(des_cs, pos_css, root_cs) \ + css_for_each_descendant_pre((pos_css), &(root_cs)->css) \ + if (is_cpuset_online(((des_cs) = css_cs((pos_css))))) /* * There are two global mutexes guarding cpuset structures - cpuset_mutex @@ -430,7 +430,7 @@ static void free_trial_cpuset(struct cpuset *trial) static int validate_change(struct cpuset *cur, struct cpuset *trial) { - struct cgroup *cgrp; + struct cgroup_subsys_state *css; struct cpuset *c, *par; int ret; @@ -438,7 +438,7 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial) /* Each of our child cpusets must be a subset of us */ ret = -EBUSY; - cpuset_for_each_child(c, cgrp, cur) + cpuset_for_each_child(c, css, cur) if (!is_cpuset_subset(c, trial)) goto out; @@ -459,7 +459,7 @@ static int validate_change(struct cpuset *cur, struct cpuset *trial) * overlap */ ret = -EINVAL; - cpuset_for_each_child(c, cgrp, par) { + cpuset_for_each_child(c, css, par) { if ((is_cpu_exclusive(trial) || is_cpu_exclusive(c)) && c != cur && cpumask_intersects(trial->cpus_allowed, c->cpus_allowed)) @@ -508,13 +508,13 @@ static void update_domain_attr_tree(struct sched_domain_attr *dattr, struct cpuset *root_cs) { struct cpuset *cp; - struct cgroup *pos_cgrp; + struct cgroup_subsys_state *pos_css; rcu_read_lock(); - cpuset_for_each_descendant_pre(cp, pos_cgrp, root_cs) { + cpuset_for_each_descendant_pre(cp, pos_css, root_cs) { /* skip the whole subtree if @cp doesn't have any CPU */ if (cpumask_empty(cp->cpus_allowed)) { - pos_cgrp = cgroup_rightmost_descendant(pos_cgrp); + pos_css = css_rightmost_descendant(pos_css); continue; } @@ -589,7 +589,7 @@ static int generate_sched_domains(cpumask_var_t **domains, struct sched_domain_attr *dattr; /* attributes for custom domains */ int ndoms = 0; /* number of sched domains in result */ int nslot; /* next empty doms[] struct cpumask slot */ - struct cgroup *pos_cgrp; + struct cgroup_subsys_state *pos_css; doms = NULL; dattr = NULL; @@ -618,7 +618,7 @@ static int generate_sched_domains(cpumask_var_t **domains, csn = 0; rcu_read_lock(); - cpuset_for_each_descendant_pre(cp, pos_cgrp, &top_cpuset) { + cpuset_for_each_descendant_pre(cp, pos_css, &top_cpuset) { /* * Continue traversing beyond @cp iff @cp has some CPUs and * isn't load balancing. The former is obvious. The @@ -635,7 +635,7 @@ static int generate_sched_domains(cpumask_var_t **domains, csa[csn++] = cp; /* skip @cp's subtree */ - pos_cgrp = cgroup_rightmost_descendant(pos_cgrp); + pos_css = css_rightmost_descendant(pos_css); } rcu_read_unlock(); @@ -886,16 +886,16 @@ static void update_tasks_cpumask_hier(struct cpuset *root_cs, bool update_root, struct ptr_heap *heap) { struct cpuset *cp; - struct cgroup *pos_cgrp; + struct cgroup_subsys_state *pos_css; if (update_root) update_tasks_cpumask(root_cs, heap); rcu_read_lock(); - cpuset_for_each_descendant_pre(cp, pos_cgrp, root_cs) { + cpuset_for_each_descendant_pre(cp, pos_css, root_cs) { /* skip the whole subtree if @cp have some CPU */ if (!cpumask_empty(cp->cpus_allowed)) { - pos_cgrp = cgroup_rightmost_descendant(pos_cgrp); + pos_css = css_rightmost_descendant(pos_css); continue; } if (!css_tryget(&cp->css)) @@ -1143,16 +1143,16 @@ static void update_tasks_nodemask_hier(struct cpuset *root_cs, bool update_root, struct ptr_heap *heap) { struct cpuset *cp; - struct cgroup *pos_cgrp; + struct cgroup_subsys_state *pos_css; if (update_root) update_tasks_nodemask(root_cs, heap); rcu_read_lock(); - cpuset_for_each_descendant_pre(cp, pos_cgrp, root_cs) { + cpuset_for_each_descendant_pre(cp, pos_css, root_cs) { /* skip the whole subtree if @cp have some CPU */ if (!nodes_empty(cp->mems_allowed)) { - pos_cgrp = cgroup_rightmost_descendant(pos_cgrp); + pos_css = css_rightmost_descendant(pos_css); continue; } if (!css_tryget(&cp->css)) @@ -1973,7 +1973,7 @@ static int cpuset_css_online(struct cgroup_subsys_state *css) struct cpuset *cs = css_cs(css); struct cpuset *parent = parent_cs(cs); struct cpuset *tmp_cs; - struct cgroup *pos_cgrp; + struct cgroup_subsys_state *pos_css; if (!parent) return 0; @@ -2005,7 +2005,7 @@ static int cpuset_css_online(struct cgroup_subsys_state *css) * (and likewise for mems) to the new cgroup. */ rcu_read_lock(); - cpuset_for_each_child(tmp_cs, pos_cgrp, parent) { + cpuset_for_each_child(tmp_cs, pos_css, parent) { if (is_mem_exclusive(tmp_cs) || is_cpu_exclusive(tmp_cs)) { rcu_read_unlock(); goto out_unlock; @@ -2252,10 +2252,10 @@ static void cpuset_hotplug_workfn(struct work_struct *work) /* if cpus or mems changed, we need to propagate to descendants */ if (cpus_updated || mems_updated) { struct cpuset *cs; - struct cgroup *pos_cgrp; + struct cgroup_subsys_state *pos_css; rcu_read_lock(); - cpuset_for_each_descendant_pre(cs, pos_cgrp, &top_cpuset) { + cpuset_for_each_descendant_pre(cs, pos_css, &top_cpuset) { if (!css_tryget(&cs->css)) continue; rcu_read_unlock(); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index ab64dfc84f8c..2285319e23a9 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1082,7 +1082,7 @@ struct mem_cgroup *try_get_mem_cgroup_from_mm(struct mm_struct *mm) static struct mem_cgroup *__mem_cgroup_iter_next(struct mem_cgroup *root, struct mem_cgroup *last_visited) { - struct cgroup *prev_cgroup, *next_cgroup; + struct cgroup_subsys_state *prev_css, *next_css; /* * Root is not visited by cgroup iterators so it needs an @@ -1091,11 +1091,9 @@ static struct mem_cgroup *__mem_cgroup_iter_next(struct mem_cgroup *root, if (!last_visited) return root; - prev_cgroup = (last_visited == root) ? NULL - : last_visited->css.cgroup; + prev_css = (last_visited == root) ? NULL : &last_visited->css; skip_node: - next_cgroup = cgroup_next_descendant_pre( - prev_cgroup, root->css.cgroup); + next_css = css_next_descendant_pre(prev_css, &root->css); /* * Even if we found a group we have to make sure it is @@ -1104,13 +1102,13 @@ skip_node: * last_visited css is safe to use because it is * protected by css_get and the tree walk is rcu safe. */ - if (next_cgroup) { - struct mem_cgroup *mem = mem_cgroup_from_cont( - next_cgroup); + if (next_css) { + struct mem_cgroup *mem = mem_cgroup_from_css(next_css); + if (css_tryget(&mem->css)) return mem; else { - prev_cgroup = next_cgroup; + prev_css = next_css; goto skip_node; } } @@ -4939,10 +4937,10 @@ static void mem_cgroup_reparent_charges(struct mem_cgroup *memcg) */ static inline bool __memcg_has_children(struct mem_cgroup *memcg) { - struct cgroup *pos; + struct cgroup_subsys_state *pos; /* bounce at first found */ - cgroup_for_each_child(pos, memcg->css.cgroup) + css_for_each_child(pos, &memcg->css) return true; return false; } diff --git a/security/device_cgroup.c b/security/device_cgroup.c index e0ca464fa854..9bf230aa28b0 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -56,11 +56,6 @@ static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s) return s ? container_of(s, struct dev_cgroup, css) : NULL; } -static inline struct dev_cgroup *cgroup_to_devcgroup(struct cgroup *cgroup) -{ - return css_to_devcgroup(cgroup_css(cgroup, devices_subsys_id)); -} - static inline struct dev_cgroup *task_devcgroup(struct task_struct *task) { return css_to_devcgroup(task_css(task, devices_subsys_id)); @@ -447,13 +442,13 @@ static void revalidate_active_exceptions(struct dev_cgroup *devcg) static int propagate_exception(struct dev_cgroup *devcg_root, struct dev_exception_item *ex) { - struct cgroup *root = devcg_root->css.cgroup, *pos; + struct cgroup_subsys_state *pos; int rc = 0; rcu_read_lock(); - cgroup_for_each_descendant_pre(pos, root) { - struct dev_cgroup *devcg = cgroup_to_devcgroup(pos); + css_for_each_descendant_pre(pos, &devcg_root->css) { + struct dev_cgroup *devcg = css_to_devcgroup(pos); /* * Because devcgroup_mutex is held, no devcg will become -- cgit v1.2.3-71-gd317 From 0942eeeef68f9493c1bcb1a52baf612b73fcf9fb Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:26 -0400 Subject: cgroup: rename cgroup_iter to cgroup_task_iter cgroup now has multiple iterators and it's quite confusing to have something which walks over tasks of a single cgroup named cgroup_iter. Let's rename it to cgroup_task_iter. While at it, reformat / update comments and replace the overview comment above the interface function decls with proper function comments. Such overview can be useful but function comments should be more than enough here. This is pure rename and doesn't introduce any functional changes. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Michal Hocko Cc: Matt Helsley Cc: Johannes Weiner Cc: Balbir Singh --- include/linux/cgroup.h | 31 ++++--------- kernel/cgroup.c | 114 ++++++++++++++++++++++++++++++++---------------- kernel/cgroup_freezer.c | 24 +++++----- mm/memcontrol.c | 10 ++--- 4 files changed, 102 insertions(+), 77 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 4bc22f4a1abb..ea439794bd9b 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -889,31 +889,16 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, for ((pos) = css_next_descendant_post(NULL, (css)); (pos); \ (pos) = css_next_descendant_post((pos), (css))) -/* A cgroup_iter should be treated as an opaque object */ -struct cgroup_iter { - struct list_head *cset_link; - struct list_head *task; +/* A cgroup_task_iter should be treated as an opaque object */ +struct cgroup_task_iter { + struct list_head *cset_link; + struct list_head *task; }; -/* - * To iterate across the tasks in a cgroup: - * - * 1) call cgroup_iter_start to initialize an iterator - * - * 2) call cgroup_iter_next() to retrieve member tasks until it - * returns NULL or until you want to end the iteration - * - * 3) call cgroup_iter_end() to destroy the iterator. - * - * Or, call cgroup_scan_tasks() to iterate through every task in a - * cgroup - cgroup_scan_tasks() holds the css_set_lock when calling - * the test_task() callback, but not while calling the process_task() - * callback. - */ -void cgroup_iter_start(struct cgroup *cgrp, struct cgroup_iter *it); -struct task_struct *cgroup_iter_next(struct cgroup *cgrp, - struct cgroup_iter *it); -void cgroup_iter_end(struct cgroup *cgrp, struct cgroup_iter *it); +void cgroup_task_iter_start(struct cgroup *cgrp, struct cgroup_task_iter *it); +struct task_struct *cgroup_task_iter_next(struct cgroup *cgrp, + struct cgroup_task_iter *it); +void cgroup_task_iter_end(struct cgroup *cgrp, struct cgroup_task_iter *it); int cgroup_scan_tasks(struct cgroup_scanner *scan); int cgroup_attach_task_all(struct task_struct *from, struct task_struct *); int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index d56d9363d4b3..15c93f9c9e57 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -367,9 +367,11 @@ static struct cgrp_cset_link init_cgrp_cset_link; static int cgroup_init_idr(struct cgroup_subsys *ss, struct cgroup_subsys_state *css); -/* css_set_lock protects the list of css_set objects, and the - * chain of tasks off each css_set. Nests outside task->alloc_lock - * due to cgroup_iter_start() */ +/* + * css_set_lock protects the list of css_set objects, and the chain of + * tasks off each css_set. Nests outside task->alloc_lock due to + * cgroup_task_iter_start(). + */ static DEFINE_RWLOCK(css_set_lock); static int css_set_count; @@ -394,10 +396,12 @@ static unsigned long css_set_hash(struct cgroup_subsys_state *css[]) return key; } -/* We don't maintain the lists running through each css_set to its - * task until after the first call to cgroup_iter_start(). This - * reduces the fork()/exit() overhead for people who have cgroups - * compiled into their kernel but not actually in use */ +/* + * We don't maintain the lists running through each css_set to its task + * until after the first call to cgroup_task_iter_start(). This reduces + * the fork()/exit() overhead for people who have cgroups compiled into + * their kernel but not actually in use. + */ static int use_task_css_set_links __read_mostly; static void __put_css_set(struct css_set *cset, int taskexit) @@ -2982,10 +2986,10 @@ int cgroup_task_count(const struct cgroup *cgrp) } /* - * To reduce the fork() overhead for systems that are not actually - * using their cgroups capability, we don't maintain the lists running - * through each css_set to its tasks until we see the list actually - * used - in other words after the first call to cgroup_iter_start(). + * To reduce the fork() overhead for systems that are not actually using + * their cgroups capability, we don't maintain the lists running through + * each css_set to its tasks until we see the list actually used - in other + * words after the first call to cgroup_task_iter_start(). */ static void cgroup_enable_task_cg_lists(void) { @@ -3199,11 +3203,15 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, } EXPORT_SYMBOL_GPL(css_next_descendant_post); -/* - * Advance a list_head iterator. The iterator should be positioned at - * the start of a css_set +/** + * cgroup_advance_task_iter - advance a task itererator to the next css_set + * @cgrp: the cgroup to walk tasks of + * @it: the iterator to advance + * + * Advance @it to the next css_set to walk. */ -static void cgroup_advance_iter(struct cgroup *cgrp, struct cgroup_iter *it) +static void cgroup_advance_task_iter(struct cgroup *cgrp, + struct cgroup_task_iter *it) { struct list_head *l = it->cset_link; struct cgrp_cset_link *link; @@ -3223,7 +3231,21 @@ static void cgroup_advance_iter(struct cgroup *cgrp, struct cgroup_iter *it) it->task = cset->tasks.next; } -void cgroup_iter_start(struct cgroup *cgrp, struct cgroup_iter *it) +/** + * cgroup_task_iter_start - initiate task iteration + * @cgrp: the cgroup to walk tasks of + * @it: the task iterator to use + * + * Initiate iteration through the tasks of @cgrp. The caller can call + * cgroup_task_iter_next() to walk through the tasks until the function + * returns NULL. On completion of iteration, cgroup_task_iter_end() must + * be called. + * + * Note that this function acquires a lock which is released when the + * iteration finishes. The caller can't sleep while iteration is in + * progress. + */ +void cgroup_task_iter_start(struct cgroup *cgrp, struct cgroup_task_iter *it) __acquires(css_set_lock) { /* @@ -3236,11 +3258,20 @@ void cgroup_iter_start(struct cgroup *cgrp, struct cgroup_iter *it) read_lock(&css_set_lock); it->cset_link = &cgrp->cset_links; - cgroup_advance_iter(cgrp, it); + cgroup_advance_task_iter(cgrp, it); } -struct task_struct *cgroup_iter_next(struct cgroup *cgrp, - struct cgroup_iter *it) +/** + * cgroup_task_iter_next - return the next task for the iterator + * @cgrp: the cgroup to walk tasks of + * @it: the task iterator being iterated + * + * The "next" function for task iteration. @it should have been + * initialized via cgroup_task_iter_start(). Returns NULL when the + * iteration reaches the end. + */ +struct task_struct *cgroup_task_iter_next(struct cgroup *cgrp, + struct cgroup_task_iter *it) { struct task_struct *res; struct list_head *l = it->task; @@ -3254,16 +3285,25 @@ struct task_struct *cgroup_iter_next(struct cgroup *cgrp, l = l->next; link = list_entry(it->cset_link, struct cgrp_cset_link, cset_link); if (l == &link->cset->tasks) { - /* We reached the end of this task list - move on to - * the next cg_cgroup_link */ - cgroup_advance_iter(cgrp, it); + /* + * We reached the end of this task list - move on to the + * next cgrp_cset_link. + */ + cgroup_advance_task_iter(cgrp, it); } else { it->task = l; } return res; } -void cgroup_iter_end(struct cgroup *cgrp, struct cgroup_iter *it) +/** + * cgroup_task_iter_end - finish task iteration + * @cgrp: the cgroup to walk tasks of + * @it: the task iterator to finish + * + * Finish task iteration started by cgroup_task_iter_start(). + */ +void cgroup_task_iter_end(struct cgroup *cgrp, struct cgroup_task_iter *it) __releases(css_set_lock) { read_unlock(&css_set_lock); @@ -3312,7 +3352,7 @@ static inline int started_after(void *p1, void *p2) * Iterate through all the tasks in a cgroup, calling test_task() for each, * and if it returns true, call process_task() for it also. * The test_task pointer may be NULL, meaning always true (select all tasks). - * Effectively duplicates cgroup_iter_{start,next,end}() + * Effectively duplicates cgroup_task_iter_{start,next,end}() * but does not lock css_set_lock for the call to process_task(). * The struct cgroup_scanner may be embedded in any structure of the caller's * creation. @@ -3333,7 +3373,7 @@ static inline int started_after(void *p1, void *p2) int cgroup_scan_tasks(struct cgroup_scanner *scan) { int retval, i; - struct cgroup_iter it; + struct cgroup_task_iter it; struct task_struct *p, *dropped; /* Never dereference latest_task, since it's not refcounted */ struct task_struct *latest_task = NULL; @@ -3368,8 +3408,8 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan) * guarantees forward progress and that we don't miss any tasks. */ heap->size = 0; - cgroup_iter_start(scan->cgrp, &it); - while ((p = cgroup_iter_next(scan->cgrp, &it))) { + cgroup_task_iter_start(scan->cgrp, &it); + while ((p = cgroup_task_iter_next(scan->cgrp, &it))) { /* * Only affect tasks that qualify per the caller's callback, * if he provided one @@ -3402,7 +3442,7 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan) * the heap and wasn't inserted */ } - cgroup_iter_end(scan->cgrp, &it); + cgroup_task_iter_end(scan->cgrp, &it); if (heap->size) { for (i = 0; i < heap->size; i++) { @@ -3608,7 +3648,7 @@ static int pidlist_array_load(struct cgroup *cgrp, enum cgroup_filetype type, pid_t *array; int length; int pid, n = 0; /* used for populating the array */ - struct cgroup_iter it; + struct cgroup_task_iter it; struct task_struct *tsk; struct cgroup_pidlist *l; @@ -3623,8 +3663,8 @@ static int pidlist_array_load(struct cgroup *cgrp, enum cgroup_filetype type, if (!array) return -ENOMEM; /* now, populate the array */ - cgroup_iter_start(cgrp, &it); - while ((tsk = cgroup_iter_next(cgrp, &it))) { + cgroup_task_iter_start(cgrp, &it); + while ((tsk = cgroup_task_iter_next(cgrp, &it))) { if (unlikely(n == length)) break; /* get tgid or pid for procs or tasks file respectively */ @@ -3635,7 +3675,7 @@ static int pidlist_array_load(struct cgroup *cgrp, enum cgroup_filetype type, if (pid > 0) /* make sure to only use valid results */ array[n++] = pid; } - cgroup_iter_end(cgrp, &it); + cgroup_task_iter_end(cgrp, &it); length = n; /* now sort & (if procs) strip out duplicates */ sort(array, length, sizeof(pid_t), cmppid, NULL); @@ -3669,7 +3709,7 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry) { int ret = -EINVAL; struct cgroup *cgrp; - struct cgroup_iter it; + struct cgroup_task_iter it; struct task_struct *tsk; /* @@ -3683,8 +3723,8 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry) ret = 0; cgrp = dentry->d_fsdata; - cgroup_iter_start(cgrp, &it); - while ((tsk = cgroup_iter_next(cgrp, &it))) { + cgroup_task_iter_start(cgrp, &it); + while ((tsk = cgroup_task_iter_next(cgrp, &it))) { switch (tsk->state) { case TASK_RUNNING: stats->nr_running++; @@ -3704,7 +3744,7 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry) break; } } - cgroup_iter_end(cgrp, &it); + cgroup_task_iter_end(cgrp, &it); err: return ret; @@ -5137,7 +5177,7 @@ void cgroup_fork(struct task_struct *child) * Adds the task to the list running through its css_set if necessary and * call the subsystem fork() callbacks. Has to be after the task is * visible on the task list in case we race with the first call to - * cgroup_iter_start() - to guarantee that the new task ends up on its + * cgroup_task_iter_start() - to guarantee that the new task ends up on its * list. */ void cgroup_post_fork(struct task_struct *child) diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 98ca48d9ceb4..c9177f8fc661 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -258,7 +258,7 @@ static void update_if_frozen(struct cgroup_subsys_state *css) { struct freezer *freezer = css_freezer(css); struct cgroup_subsys_state *pos; - struct cgroup_iter it; + struct cgroup_task_iter it; struct task_struct *task; WARN_ON_ONCE(!rcu_read_lock_held()); @@ -279,9 +279,9 @@ static void update_if_frozen(struct cgroup_subsys_state *css) } /* are all tasks frozen? */ - cgroup_iter_start(css->cgroup, &it); + cgroup_task_iter_start(css->cgroup, &it); - while ((task = cgroup_iter_next(css->cgroup, &it))) { + while ((task = cgroup_task_iter_next(css->cgroup, &it))) { if (freezing(task)) { /* * freezer_should_skip() indicates that the task @@ -296,7 +296,7 @@ static void update_if_frozen(struct cgroup_subsys_state *css) freezer->state |= CGROUP_FROZEN; out_iter_end: - cgroup_iter_end(css->cgroup, &it); + cgroup_task_iter_end(css->cgroup, &it); out_unlock: spin_unlock_irq(&freezer->lock); } @@ -323,25 +323,25 @@ static int freezer_read(struct cgroup_subsys_state *css, struct cftype *cft, static void freeze_cgroup(struct freezer *freezer) { struct cgroup *cgroup = freezer->css.cgroup; - struct cgroup_iter it; + struct cgroup_task_iter it; struct task_struct *task; - cgroup_iter_start(cgroup, &it); - while ((task = cgroup_iter_next(cgroup, &it))) + cgroup_task_iter_start(cgroup, &it); + while ((task = cgroup_task_iter_next(cgroup, &it))) freeze_task(task); - cgroup_iter_end(cgroup, &it); + cgroup_task_iter_end(cgroup, &it); } static void unfreeze_cgroup(struct freezer *freezer) { struct cgroup *cgroup = freezer->css.cgroup; - struct cgroup_iter it; + struct cgroup_task_iter it; struct task_struct *task; - cgroup_iter_start(cgroup, &it); - while ((task = cgroup_iter_next(cgroup, &it))) + cgroup_task_iter_start(cgroup, &it); + while ((task = cgroup_task_iter_next(cgroup, &it))) __thaw_task(task); - cgroup_iter_end(cgroup, &it); + cgroup_task_iter_end(cgroup, &it); } /** diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 2285319e23a9..00b055dd1d3d 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1800,11 +1800,11 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, totalpages = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1; for_each_mem_cgroup_tree(iter, memcg) { struct cgroup *cgroup = iter->css.cgroup; - struct cgroup_iter it; + struct cgroup_task_iter it; struct task_struct *task; - cgroup_iter_start(cgroup, &it); - while ((task = cgroup_iter_next(cgroup, &it))) { + cgroup_task_iter_start(cgroup, &it); + while ((task = cgroup_task_iter_next(cgroup, &it))) { switch (oom_scan_process_thread(task, totalpages, NULL, false)) { case OOM_SCAN_SELECT: @@ -1817,7 +1817,7 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, case OOM_SCAN_CONTINUE: continue; case OOM_SCAN_ABORT: - cgroup_iter_end(cgroup, &it); + cgroup_task_iter_end(cgroup, &it); mem_cgroup_iter_break(memcg, iter); if (chosen) put_task_struct(chosen); @@ -1834,7 +1834,7 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, get_task_struct(chosen); } } - cgroup_iter_end(cgroup, &it); + cgroup_task_iter_end(cgroup, &it); } if (!chosen) -- cgit v1.2.3-71-gd317 From c59cd3d840b1b0a8f996cbbd9132128dcaabbeb9 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:26 -0400 Subject: cgroup: make cgroup_task_iter remember the cgroup being iterated Currently all cgroup_task_iter functions require @cgrp to be passed in, which is superflous and increases chance of usage error. Make cgroup_task_iter remember the cgroup being iterated and drop @cgrp argument from next and end functions. This patch doesn't introduce any behavior differences. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Michal Hocko Cc: Matt Helsley Cc: Johannes Weiner Cc: Balbir Singh --- include/linux/cgroup.h | 6 +++--- kernel/cgroup.c | 32 +++++++++++++++----------------- kernel/cgroup_freezer.c | 12 ++++++------ mm/memcontrol.c | 6 +++--- 4 files changed, 27 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index ea439794bd9b..0287fccd0f54 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -891,14 +891,14 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, /* A cgroup_task_iter should be treated as an opaque object */ struct cgroup_task_iter { + struct cgroup *origin_cgrp; struct list_head *cset_link; struct list_head *task; }; void cgroup_task_iter_start(struct cgroup *cgrp, struct cgroup_task_iter *it); -struct task_struct *cgroup_task_iter_next(struct cgroup *cgrp, - struct cgroup_task_iter *it); -void cgroup_task_iter_end(struct cgroup *cgrp, struct cgroup_task_iter *it); +struct task_struct *cgroup_task_iter_next(struct cgroup_task_iter *it); +void cgroup_task_iter_end(struct cgroup_task_iter *it); int cgroup_scan_tasks(struct cgroup_scanner *scan); int cgroup_attach_task_all(struct task_struct *from, struct task_struct *); int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 15c93f9c9e57..abc62ea1303c 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -3205,13 +3205,11 @@ EXPORT_SYMBOL_GPL(css_next_descendant_post); /** * cgroup_advance_task_iter - advance a task itererator to the next css_set - * @cgrp: the cgroup to walk tasks of * @it: the iterator to advance * * Advance @it to the next css_set to walk. */ -static void cgroup_advance_task_iter(struct cgroup *cgrp, - struct cgroup_task_iter *it) +static void cgroup_advance_task_iter(struct cgroup_task_iter *it) { struct list_head *l = it->cset_link; struct cgrp_cset_link *link; @@ -3220,7 +3218,7 @@ static void cgroup_advance_task_iter(struct cgroup *cgrp, /* Advance to the next non-empty css_set */ do { l = l->next; - if (l == &cgrp->cset_links) { + if (l == &it->origin_cgrp->cset_links) { it->cset_link = NULL; return; } @@ -3257,21 +3255,22 @@ void cgroup_task_iter_start(struct cgroup *cgrp, struct cgroup_task_iter *it) cgroup_enable_task_cg_lists(); read_lock(&css_set_lock); + + it->origin_cgrp = cgrp; it->cset_link = &cgrp->cset_links; - cgroup_advance_task_iter(cgrp, it); + + cgroup_advance_task_iter(it); } /** * cgroup_task_iter_next - return the next task for the iterator - * @cgrp: the cgroup to walk tasks of * @it: the task iterator being iterated * * The "next" function for task iteration. @it should have been * initialized via cgroup_task_iter_start(). Returns NULL when the * iteration reaches the end. */ -struct task_struct *cgroup_task_iter_next(struct cgroup *cgrp, - struct cgroup_task_iter *it) +struct task_struct *cgroup_task_iter_next(struct cgroup_task_iter *it) { struct task_struct *res; struct list_head *l = it->task; @@ -3289,7 +3288,7 @@ struct task_struct *cgroup_task_iter_next(struct cgroup *cgrp, * We reached the end of this task list - move on to the * next cgrp_cset_link. */ - cgroup_advance_task_iter(cgrp, it); + cgroup_advance_task_iter(it); } else { it->task = l; } @@ -3298,12 +3297,11 @@ struct task_struct *cgroup_task_iter_next(struct cgroup *cgrp, /** * cgroup_task_iter_end - finish task iteration - * @cgrp: the cgroup to walk tasks of * @it: the task iterator to finish * * Finish task iteration started by cgroup_task_iter_start(). */ -void cgroup_task_iter_end(struct cgroup *cgrp, struct cgroup_task_iter *it) +void cgroup_task_iter_end(struct cgroup_task_iter *it) __releases(css_set_lock) { read_unlock(&css_set_lock); @@ -3409,7 +3407,7 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan) */ heap->size = 0; cgroup_task_iter_start(scan->cgrp, &it); - while ((p = cgroup_task_iter_next(scan->cgrp, &it))) { + while ((p = cgroup_task_iter_next(&it))) { /* * Only affect tasks that qualify per the caller's callback, * if he provided one @@ -3442,7 +3440,7 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan) * the heap and wasn't inserted */ } - cgroup_task_iter_end(scan->cgrp, &it); + cgroup_task_iter_end(&it); if (heap->size) { for (i = 0; i < heap->size; i++) { @@ -3664,7 +3662,7 @@ static int pidlist_array_load(struct cgroup *cgrp, enum cgroup_filetype type, return -ENOMEM; /* now, populate the array */ cgroup_task_iter_start(cgrp, &it); - while ((tsk = cgroup_task_iter_next(cgrp, &it))) { + while ((tsk = cgroup_task_iter_next(&it))) { if (unlikely(n == length)) break; /* get tgid or pid for procs or tasks file respectively */ @@ -3675,7 +3673,7 @@ static int pidlist_array_load(struct cgroup *cgrp, enum cgroup_filetype type, if (pid > 0) /* make sure to only use valid results */ array[n++] = pid; } - cgroup_task_iter_end(cgrp, &it); + cgroup_task_iter_end(&it); length = n; /* now sort & (if procs) strip out duplicates */ sort(array, length, sizeof(pid_t), cmppid, NULL); @@ -3724,7 +3722,7 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry) cgrp = dentry->d_fsdata; cgroup_task_iter_start(cgrp, &it); - while ((tsk = cgroup_task_iter_next(cgrp, &it))) { + while ((tsk = cgroup_task_iter_next(&it))) { switch (tsk->state) { case TASK_RUNNING: stats->nr_running++; @@ -3744,7 +3742,7 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry) break; } } - cgroup_task_iter_end(cgrp, &it); + cgroup_task_iter_end(&it); err: return ret; diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index c9177f8fc661..e0ab9bfd679a 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -281,7 +281,7 @@ static void update_if_frozen(struct cgroup_subsys_state *css) /* are all tasks frozen? */ cgroup_task_iter_start(css->cgroup, &it); - while ((task = cgroup_task_iter_next(css->cgroup, &it))) { + while ((task = cgroup_task_iter_next(&it))) { if (freezing(task)) { /* * freezer_should_skip() indicates that the task @@ -296,7 +296,7 @@ static void update_if_frozen(struct cgroup_subsys_state *css) freezer->state |= CGROUP_FROZEN; out_iter_end: - cgroup_task_iter_end(css->cgroup, &it); + cgroup_task_iter_end(&it); out_unlock: spin_unlock_irq(&freezer->lock); } @@ -327,9 +327,9 @@ static void freeze_cgroup(struct freezer *freezer) struct task_struct *task; cgroup_task_iter_start(cgroup, &it); - while ((task = cgroup_task_iter_next(cgroup, &it))) + while ((task = cgroup_task_iter_next(&it))) freeze_task(task); - cgroup_task_iter_end(cgroup, &it); + cgroup_task_iter_end(&it); } static void unfreeze_cgroup(struct freezer *freezer) @@ -339,9 +339,9 @@ static void unfreeze_cgroup(struct freezer *freezer) struct task_struct *task; cgroup_task_iter_start(cgroup, &it); - while ((task = cgroup_task_iter_next(cgroup, &it))) + while ((task = cgroup_task_iter_next(&it))) __thaw_task(task); - cgroup_task_iter_end(cgroup, &it); + cgroup_task_iter_end(&it); } /** diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 00b055dd1d3d..5a5f4dc649f0 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1804,7 +1804,7 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, struct task_struct *task; cgroup_task_iter_start(cgroup, &it); - while ((task = cgroup_task_iter_next(cgroup, &it))) { + while ((task = cgroup_task_iter_next(&it))) { switch (oom_scan_process_thread(task, totalpages, NULL, false)) { case OOM_SCAN_SELECT: @@ -1817,7 +1817,7 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, case OOM_SCAN_CONTINUE: continue; case OOM_SCAN_ABORT: - cgroup_task_iter_end(cgroup, &it); + cgroup_task_iter_end(&it); mem_cgroup_iter_break(memcg, iter); if (chosen) put_task_struct(chosen); @@ -1834,7 +1834,7 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, get_task_struct(chosen); } } - cgroup_task_iter_end(cgroup, &it); + cgroup_task_iter_end(&it); } if (!chosen) -- cgit v1.2.3-71-gd317 From e535837b1dae17b5a2d76ea1bc22ac1a79354624 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:26 -0400 Subject: cgroup: remove struct cgroup_scanner cgroup_scan_tasks() takes a pointer to struct cgroup_scanner as its sole argument and the only function of that struct is packing the arguments of the function call which are consisted of five fields. It's not too unusual to pack parameters into a struct when the number of arguments gets excessive or the whole set needs to be passed around a lot, but neither holds here making it just weird. Drop struct cgroup_scanner and pass the params directly to cgroup_scan_tasks(). Note that struct cpuset_change_nodemask_arg was added to cpuset.c to pass both ->cs and ->newmems pointer to cpuset_change_nodemask() using single data pointer. This doesn't make any functional differences. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 16 ++++----- kernel/cgroup.c | 93 +++++++++++++++++++++++--------------------------- kernel/cpuset.c | 63 ++++++++++++++-------------------- 3 files changed, 75 insertions(+), 97 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 0287fccd0f54..8472ed576b64 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -528,15 +528,6 @@ struct cftype_set { struct cftype *cfts; }; -struct cgroup_scanner { - struct cgroup *cgrp; - int (*test_task)(struct task_struct *p, struct cgroup_scanner *scan); - void (*process_task)(struct task_struct *p, - struct cgroup_scanner *scan); - struct ptr_heap *heap; - void *data; -}; - /* * See the comment above CGRP_ROOT_SANE_BEHAVIOR for details. This * function can be called as long as @cgrp is accessible. @@ -899,7 +890,12 @@ struct cgroup_task_iter { void cgroup_task_iter_start(struct cgroup *cgrp, struct cgroup_task_iter *it); struct task_struct *cgroup_task_iter_next(struct cgroup_task_iter *it); void cgroup_task_iter_end(struct cgroup_task_iter *it); -int cgroup_scan_tasks(struct cgroup_scanner *scan); + +int cgroup_scan_tasks(struct cgroup *cgrp, + bool (*test)(struct task_struct *, void *), + void (*process)(struct task_struct *, void *), + void *data, struct ptr_heap *heap); + int cgroup_attach_task_all(struct task_struct *from, struct task_struct *); int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index abc62ea1303c..7b16ddb2569b 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -3343,32 +3343,37 @@ static inline int started_after(void *p1, void *p2) /** * cgroup_scan_tasks - iterate though all the tasks in a cgroup - * @scan: struct cgroup_scanner containing arguments for the scan + * @cgrp: the cgroup to iterate tasks of + * @test: optional test callback + * @process: process callback + * @data: data passed to @test and @process + * @heap: optional pre-allocated heap used for task iteration * - * Arguments include pointers to callback functions test_task() and - * process_task(). - * Iterate through all the tasks in a cgroup, calling test_task() for each, - * and if it returns true, call process_task() for it also. - * The test_task pointer may be NULL, meaning always true (select all tasks). - * Effectively duplicates cgroup_task_iter_{start,next,end}() - * but does not lock css_set_lock for the call to process_task(). - * The struct cgroup_scanner may be embedded in any structure of the caller's - * creation. - * It is guaranteed that process_task() will act on every task that - * is a member of the cgroup for the duration of this call. This - * function may or may not call process_task() for tasks that exit - * or move to a different cgroup during the call, or are forked or - * move into the cgroup during the call. + * Iterate through all the tasks in a cgroup, calling @test for each, and + * if it returns %true, call @process for it also. * - * Note that test_task() may be called with locks held, and may in some - * situations be called multiple times for the same task, so it should - * be cheap. - * If the heap pointer in the struct cgroup_scanner is non-NULL, a heap has been - * pre-allocated and will be used for heap operations (and its "gt" member will - * be overwritten), else a temporary heap will be used (allocation of which - * may cause this function to fail). + * @test may be NULL, meaning always true (select all tasks), which + * effectively duplicates cgroup_task_iter_{start,next,end}() but does not + * lock css_set_lock for the call to @process. + * + * It is guaranteed that @process will act on every task that is a member + * of @cgrp for the duration of this call. This function may or may not + * call @process for tasks that exit or move to a different cgroup during + * the call, or are forked or move into the cgroup during the call. + * + * Note that @test may be called with locks held, and may in some + * situations be called multiple times for the same task, so it should be + * cheap. + * + * If @heap is non-NULL, a heap has been pre-allocated and will be used for + * heap operations (and its "gt" member will be overwritten), else a + * temporary heap will be used (allocation of which may cause this function + * to fail). */ -int cgroup_scan_tasks(struct cgroup_scanner *scan) +int cgroup_scan_tasks(struct cgroup *cgrp, + bool (*test)(struct task_struct *, void *), + void (*process)(struct task_struct *, void *), + void *data, struct ptr_heap *heap) { int retval, i; struct cgroup_task_iter it; @@ -3376,12 +3381,10 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan) /* Never dereference latest_task, since it's not refcounted */ struct task_struct *latest_task = NULL; struct ptr_heap tmp_heap; - struct ptr_heap *heap; struct timespec latest_time = { 0, 0 }; - if (scan->heap) { + if (heap) { /* The caller supplied our heap and pre-allocated its memory */ - heap = scan->heap; heap->gt = &started_after; } else { /* We need to allocate our own heap memory */ @@ -3394,25 +3397,24 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan) again: /* - * Scan tasks in the cgroup, using the scanner's "test_task" callback - * to determine which are of interest, and using the scanner's - * "process_task" callback to process any of them that need an update. - * Since we don't want to hold any locks during the task updates, - * gather tasks to be processed in a heap structure. - * The heap is sorted by descending task start time. - * If the statically-sized heap fills up, we overflow tasks that - * started later, and in future iterations only consider tasks that - * started after the latest task in the previous pass. This + * Scan tasks in the cgroup, using the @test callback to determine + * which are of interest, and invoking @process callback on the + * ones which need an update. Since we don't want to hold any + * locks during the task updates, gather tasks to be processed in a + * heap structure. The heap is sorted by descending task start + * time. If the statically-sized heap fills up, we overflow tasks + * that started later, and in future iterations only consider tasks + * that started after the latest task in the previous pass. This * guarantees forward progress and that we don't miss any tasks. */ heap->size = 0; - cgroup_task_iter_start(scan->cgrp, &it); + cgroup_task_iter_start(cgrp, &it); while ((p = cgroup_task_iter_next(&it))) { /* * Only affect tasks that qualify per the caller's callback, * if he provided one */ - if (scan->test_task && !scan->test_task(p, scan)) + if (test && !test(p, data)) continue; /* * Only process tasks that started after the last task @@ -3450,7 +3452,7 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan) latest_task = q; } /* Process the task per the caller's callback */ - scan->process_task(q, scan); + process(q, data); put_task_struct(q); } /* @@ -3467,10 +3469,9 @@ int cgroup_scan_tasks(struct cgroup_scanner *scan) return 0; } -static void cgroup_transfer_one_task(struct task_struct *task, - struct cgroup_scanner *scan) +static void cgroup_transfer_one_task(struct task_struct *task, void *data) { - struct cgroup *new_cgroup = scan->data; + struct cgroup *new_cgroup = data; mutex_lock(&cgroup_mutex); cgroup_attach_task(new_cgroup, task, false); @@ -3484,15 +3485,7 @@ static void cgroup_transfer_one_task(struct task_struct *task, */ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) { - struct cgroup_scanner scan; - - scan.cgrp = from; - scan.test_task = NULL; /* select all tasks in cgroup */ - scan.process_task = cgroup_transfer_one_task; - scan.heap = NULL; - scan.data = to; - - return cgroup_scan_tasks(&scan); + return cgroup_scan_tasks(from, NULL, cgroup_transfer_one_task, to, NULL); } /* diff --git a/kernel/cpuset.c b/kernel/cpuset.c index be4f5036ea5e..6fe23f2ac742 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -830,7 +830,7 @@ static struct cpuset *effective_nodemask_cpuset(struct cpuset *cs) /** * cpuset_change_cpumask - make a task's cpus_allowed the same as its cpuset's * @tsk: task to test - * @scan: struct cgroup_scanner containing the cgroup of the task + * @data: cpuset to @tsk belongs to * * Called by cgroup_scan_tasks() for each task in a cgroup whose * cpus_allowed mask needs to be changed. @@ -838,12 +838,11 @@ static struct cpuset *effective_nodemask_cpuset(struct cpuset *cs) * We don't need to re-check for the cgroup/cpuset membership, since we're * holding cpuset_mutex at this point. */ -static void cpuset_change_cpumask(struct task_struct *tsk, - struct cgroup_scanner *scan) +static void cpuset_change_cpumask(struct task_struct *tsk, void *data) { - struct cpuset *cpus_cs; + struct cpuset *cs = data; + struct cpuset *cpus_cs = effective_cpumask_cpuset(cs); - cpus_cs = effective_cpumask_cpuset(cgroup_cs(scan->cgrp)); set_cpus_allowed_ptr(tsk, cpus_cs->cpus_allowed); } @@ -862,13 +861,8 @@ static void cpuset_change_cpumask(struct task_struct *tsk, */ static void update_tasks_cpumask(struct cpuset *cs, struct ptr_heap *heap) { - struct cgroup_scanner scan; - - scan.cgrp = cs->css.cgroup; - scan.test_task = NULL; - scan.process_task = cpuset_change_cpumask; - scan.heap = heap; - cgroup_scan_tasks(&scan); + cgroup_scan_tasks(cs->css.cgroup, NULL, cpuset_change_cpumask, cs, + heap); } /* @@ -1052,20 +1046,24 @@ static void cpuset_change_task_nodemask(struct task_struct *tsk, task_unlock(tsk); } +struct cpuset_change_nodemask_arg { + struct cpuset *cs; + nodemask_t *newmems; +}; + /* * Update task's mems_allowed and rebind its mempolicy and vmas' mempolicy * of it to cpuset's new mems_allowed, and migrate pages to new nodes if * memory_migrate flag is set. Called with cpuset_mutex held. */ -static void cpuset_change_nodemask(struct task_struct *p, - struct cgroup_scanner *scan) +static void cpuset_change_nodemask(struct task_struct *p, void *data) { - struct cpuset *cs = cgroup_cs(scan->cgrp); + struct cpuset_change_nodemask_arg *arg = data; + struct cpuset *cs = arg->cs; struct mm_struct *mm; int migrate; - nodemask_t *newmems = scan->data; - cpuset_change_task_nodemask(p, newmems); + cpuset_change_task_nodemask(p, arg->newmems); mm = get_task_mm(p); if (!mm) @@ -1075,7 +1073,7 @@ static void cpuset_change_nodemask(struct task_struct *p, mpol_rebind_mm(mm, &cs->mems_allowed); if (migrate) - cpuset_migrate_mm(mm, &cs->old_mems_allowed, newmems); + cpuset_migrate_mm(mm, &cs->old_mems_allowed, arg->newmems); mmput(mm); } @@ -1093,19 +1091,14 @@ static void *cpuset_being_rebound; static void update_tasks_nodemask(struct cpuset *cs, struct ptr_heap *heap) { static nodemask_t newmems; /* protected by cpuset_mutex */ - struct cgroup_scanner scan; struct cpuset *mems_cs = effective_nodemask_cpuset(cs); + struct cpuset_change_nodemask_arg arg = { .cs = cs, + .newmems = &newmems }; cpuset_being_rebound = cs; /* causes mpol_dup() rebind */ guarantee_online_mems(mems_cs, &newmems); - scan.cgrp = cs->css.cgroup; - scan.test_task = NULL; - scan.process_task = cpuset_change_nodemask; - scan.heap = heap; - scan.data = &newmems; - /* * The mpol_rebind_mm() call takes mmap_sem, which we couldn't * take while holding tasklist_lock. Forks can happen - the @@ -1116,7 +1109,8 @@ static void update_tasks_nodemask(struct cpuset *cs, struct ptr_heap *heap) * It's ok if we rebind the same mm twice; mpol_rebind_mm() * is idempotent. Also migrate pages in each mm to new nodes. */ - cgroup_scan_tasks(&scan); + cgroup_scan_tasks(cs->css.cgroup, NULL, cpuset_change_nodemask, &arg, + heap); /* * All the tasks' nodemasks have been updated, update @@ -1263,17 +1257,18 @@ static int update_relax_domain_level(struct cpuset *cs, s64 val) /* * cpuset_change_flag - make a task's spread flags the same as its cpuset's * @tsk: task to be updated - * @scan: struct cgroup_scanner containing the cgroup of the task + * @data: cpuset to @tsk belongs to * * Called by cgroup_scan_tasks() for each task in a cgroup. * * We don't need to re-check for the cgroup/cpuset membership, since we're * holding cpuset_mutex at this point. */ -static void cpuset_change_flag(struct task_struct *tsk, - struct cgroup_scanner *scan) +static void cpuset_change_flag(struct task_struct *tsk, void *data) { - cpuset_update_task_spread_flag(cgroup_cs(scan->cgrp), tsk); + struct cpuset *cs = data; + + cpuset_update_task_spread_flag(cs, tsk); } /* @@ -1291,13 +1286,7 @@ static void cpuset_change_flag(struct task_struct *tsk, */ static void update_tasks_flags(struct cpuset *cs, struct ptr_heap *heap) { - struct cgroup_scanner scan; - - scan.cgrp = cs->css.cgroup; - scan.test_task = NULL; - scan.process_task = cpuset_change_flag; - scan.heap = heap; - cgroup_scan_tasks(&scan); + cgroup_scan_tasks(cs->css.cgroup, NULL, cpuset_change_flag, cs, heap); } /* -- cgit v1.2.3-71-gd317 From 72ec7029937f0518eff21b8762743c31591684f5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:26 -0400 Subject: cgroup: make task iterators deal with cgroup_subsys_state instead of cgroup cgroup is in the process of converting to css (cgroup_subsys_state) from cgroup as the principal subsystem interface handle. This is mostly to prepare for the unified hierarchy support where css's will be created and destroyed dynamically but also helps cleaning up subsystem implementations as css is usually what they are interested in anyway. This patch converts task iterators to deal with css instead of cgroup. Note that under unified hierarchy, different sets of tasks will be considered belonging to a given cgroup depending on the subsystem in question and making the iterators deal with css instead cgroup provides them with enough information about the iteration. While at it, fix several function comment formats in cpuset.c. This patch doesn't introduce any behavior differences. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Michal Hocko Cc: Johannes Weiner Cc: Balbir Singh Cc: Matt Helsley --- include/linux/cgroup.h | 21 ++++----- kernel/cgroup.c | 112 ++++++++++++++++++++++++------------------------ kernel/cgroup_freezer.c | 26 ++++++----- kernel/cpuset.c | 41 ++++++++---------- mm/memcontrol.c | 11 +++-- 5 files changed, 104 insertions(+), 107 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 8472ed576b64..cd105fce089c 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -880,21 +880,22 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, for ((pos) = css_next_descendant_post(NULL, (css)); (pos); \ (pos) = css_next_descendant_post((pos), (css))) -/* A cgroup_task_iter should be treated as an opaque object */ -struct cgroup_task_iter { - struct cgroup *origin_cgrp; +/* A css_task_iter should be treated as an opaque object */ +struct css_task_iter { + struct cgroup_subsys_state *origin_css; struct list_head *cset_link; struct list_head *task; }; -void cgroup_task_iter_start(struct cgroup *cgrp, struct cgroup_task_iter *it); -struct task_struct *cgroup_task_iter_next(struct cgroup_task_iter *it); -void cgroup_task_iter_end(struct cgroup_task_iter *it); +void css_task_iter_start(struct cgroup_subsys_state *css, + struct css_task_iter *it); +struct task_struct *css_task_iter_next(struct css_task_iter *it); +void css_task_iter_end(struct css_task_iter *it); -int cgroup_scan_tasks(struct cgroup *cgrp, - bool (*test)(struct task_struct *, void *), - void (*process)(struct task_struct *, void *), - void *data, struct ptr_heap *heap); +int css_scan_tasks(struct cgroup_subsys_state *css, + bool (*test)(struct task_struct *, void *), + void (*process)(struct task_struct *, void *), + void *data, struct ptr_heap *heap); int cgroup_attach_task_all(struct task_struct *from, struct task_struct *); int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from); diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 7b16ddb2569b..8c57301d0561 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -370,7 +370,7 @@ static int cgroup_init_idr(struct cgroup_subsys *ss, /* * css_set_lock protects the list of css_set objects, and the chain of * tasks off each css_set. Nests outside task->alloc_lock due to - * cgroup_task_iter_start(). + * css_task_iter_start(). */ static DEFINE_RWLOCK(css_set_lock); static int css_set_count; @@ -398,9 +398,9 @@ static unsigned long css_set_hash(struct cgroup_subsys_state *css[]) /* * We don't maintain the lists running through each css_set to its task - * until after the first call to cgroup_task_iter_start(). This reduces - * the fork()/exit() overhead for people who have cgroups compiled into - * their kernel but not actually in use. + * until after the first call to css_task_iter_start(). This reduces the + * fork()/exit() overhead for people who have cgroups compiled into their + * kernel but not actually in use. */ static int use_task_css_set_links __read_mostly; @@ -2989,7 +2989,7 @@ int cgroup_task_count(const struct cgroup *cgrp) * To reduce the fork() overhead for systems that are not actually using * their cgroups capability, we don't maintain the lists running through * each css_set to its tasks until we see the list actually used - in other - * words after the first call to cgroup_task_iter_start(). + * words after the first call to css_task_iter_start(). */ static void cgroup_enable_task_cg_lists(void) { @@ -3204,12 +3204,12 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, EXPORT_SYMBOL_GPL(css_next_descendant_post); /** - * cgroup_advance_task_iter - advance a task itererator to the next css_set + * css_advance_task_iter - advance a task itererator to the next css_set * @it: the iterator to advance * * Advance @it to the next css_set to walk. */ -static void cgroup_advance_task_iter(struct cgroup_task_iter *it) +static void css_advance_task_iter(struct css_task_iter *it) { struct list_head *l = it->cset_link; struct cgrp_cset_link *link; @@ -3218,7 +3218,7 @@ static void cgroup_advance_task_iter(struct cgroup_task_iter *it) /* Advance to the next non-empty css_set */ do { l = l->next; - if (l == &it->origin_cgrp->cset_links) { + if (l == &it->origin_css->cgroup->cset_links) { it->cset_link = NULL; return; } @@ -3230,47 +3230,48 @@ static void cgroup_advance_task_iter(struct cgroup_task_iter *it) } /** - * cgroup_task_iter_start - initiate task iteration - * @cgrp: the cgroup to walk tasks of + * css_task_iter_start - initiate task iteration + * @css: the css to walk tasks of * @it: the task iterator to use * - * Initiate iteration through the tasks of @cgrp. The caller can call - * cgroup_task_iter_next() to walk through the tasks until the function - * returns NULL. On completion of iteration, cgroup_task_iter_end() must - * be called. + * Initiate iteration through the tasks of @css. The caller can call + * css_task_iter_next() to walk through the tasks until the function + * returns NULL. On completion of iteration, css_task_iter_end() must be + * called. * * Note that this function acquires a lock which is released when the * iteration finishes. The caller can't sleep while iteration is in * progress. */ -void cgroup_task_iter_start(struct cgroup *cgrp, struct cgroup_task_iter *it) +void css_task_iter_start(struct cgroup_subsys_state *css, + struct css_task_iter *it) __acquires(css_set_lock) { /* - * The first time anyone tries to iterate across a cgroup, - * we need to enable the list linking each css_set to its - * tasks, and fix up all existing tasks. + * The first time anyone tries to iterate across a css, we need to + * enable the list linking each css_set to its tasks, and fix up + * all existing tasks. */ if (!use_task_css_set_links) cgroup_enable_task_cg_lists(); read_lock(&css_set_lock); - it->origin_cgrp = cgrp; - it->cset_link = &cgrp->cset_links; + it->origin_css = css; + it->cset_link = &css->cgroup->cset_links; - cgroup_advance_task_iter(it); + css_advance_task_iter(it); } /** - * cgroup_task_iter_next - return the next task for the iterator + * css_task_iter_next - return the next task for the iterator * @it: the task iterator being iterated * * The "next" function for task iteration. @it should have been - * initialized via cgroup_task_iter_start(). Returns NULL when the - * iteration reaches the end. + * initialized via css_task_iter_start(). Returns NULL when the iteration + * reaches the end. */ -struct task_struct *cgroup_task_iter_next(struct cgroup_task_iter *it) +struct task_struct *css_task_iter_next(struct css_task_iter *it) { struct task_struct *res; struct list_head *l = it->task; @@ -3288,7 +3289,7 @@ struct task_struct *cgroup_task_iter_next(struct cgroup_task_iter *it) * We reached the end of this task list - move on to the * next cgrp_cset_link. */ - cgroup_advance_task_iter(it); + css_advance_task_iter(it); } else { it->task = l; } @@ -3296,12 +3297,12 @@ struct task_struct *cgroup_task_iter_next(struct cgroup_task_iter *it) } /** - * cgroup_task_iter_end - finish task iteration + * css_task_iter_end - finish task iteration * @it: the task iterator to finish * - * Finish task iteration started by cgroup_task_iter_start(). + * Finish task iteration started by css_task_iter_start(). */ -void cgroup_task_iter_end(struct cgroup_task_iter *it) +void css_task_iter_end(struct css_task_iter *it) __releases(css_set_lock) { read_unlock(&css_set_lock); @@ -3342,24 +3343,24 @@ static inline int started_after(void *p1, void *p2) } /** - * cgroup_scan_tasks - iterate though all the tasks in a cgroup - * @cgrp: the cgroup to iterate tasks of + * css_scan_tasks - iterate though all the tasks in a css + * @css: the css to iterate tasks of * @test: optional test callback * @process: process callback * @data: data passed to @test and @process * @heap: optional pre-allocated heap used for task iteration * - * Iterate through all the tasks in a cgroup, calling @test for each, and - * if it returns %true, call @process for it also. + * Iterate through all the tasks in @css, calling @test for each, and if it + * returns %true, call @process for it also. * * @test may be NULL, meaning always true (select all tasks), which - * effectively duplicates cgroup_task_iter_{start,next,end}() but does not + * effectively duplicates css_task_iter_{start,next,end}() but does not * lock css_set_lock for the call to @process. * * It is guaranteed that @process will act on every task that is a member - * of @cgrp for the duration of this call. This function may or may not - * call @process for tasks that exit or move to a different cgroup during - * the call, or are forked or move into the cgroup during the call. + * of @css for the duration of this call. This function may or may not + * call @process for tasks that exit or move to a different css during the + * call, or are forked or move into the css during the call. * * Note that @test may be called with locks held, and may in some * situations be called multiple times for the same task, so it should be @@ -3370,13 +3371,13 @@ static inline int started_after(void *p1, void *p2) * temporary heap will be used (allocation of which may cause this function * to fail). */ -int cgroup_scan_tasks(struct cgroup *cgrp, - bool (*test)(struct task_struct *, void *), - void (*process)(struct task_struct *, void *), - void *data, struct ptr_heap *heap) +int css_scan_tasks(struct cgroup_subsys_state *css, + bool (*test)(struct task_struct *, void *), + void (*process)(struct task_struct *, void *), + void *data, struct ptr_heap *heap) { int retval, i; - struct cgroup_task_iter it; + struct css_task_iter it; struct task_struct *p, *dropped; /* Never dereference latest_task, since it's not refcounted */ struct task_struct *latest_task = NULL; @@ -3397,7 +3398,7 @@ int cgroup_scan_tasks(struct cgroup *cgrp, again: /* - * Scan tasks in the cgroup, using the @test callback to determine + * Scan tasks in the css, using the @test callback to determine * which are of interest, and invoking @process callback on the * ones which need an update. Since we don't want to hold any * locks during the task updates, gather tasks to be processed in a @@ -3408,8 +3409,8 @@ int cgroup_scan_tasks(struct cgroup *cgrp, * guarantees forward progress and that we don't miss any tasks. */ heap->size = 0; - cgroup_task_iter_start(cgrp, &it); - while ((p = cgroup_task_iter_next(&it))) { + css_task_iter_start(css, &it); + while ((p = css_task_iter_next(&it))) { /* * Only affect tasks that qualify per the caller's callback, * if he provided one @@ -3442,7 +3443,7 @@ int cgroup_scan_tasks(struct cgroup *cgrp, * the heap and wasn't inserted */ } - cgroup_task_iter_end(&it); + css_task_iter_end(&it); if (heap->size) { for (i = 0; i < heap->size; i++) { @@ -3485,7 +3486,8 @@ static void cgroup_transfer_one_task(struct task_struct *task, void *data) */ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) { - return cgroup_scan_tasks(from, NULL, cgroup_transfer_one_task, to, NULL); + return css_scan_tasks(&from->dummy_css, NULL, cgroup_transfer_one_task, + to, NULL); } /* @@ -3639,7 +3641,7 @@ static int pidlist_array_load(struct cgroup *cgrp, enum cgroup_filetype type, pid_t *array; int length; int pid, n = 0; /* used for populating the array */ - struct cgroup_task_iter it; + struct css_task_iter it; struct task_struct *tsk; struct cgroup_pidlist *l; @@ -3654,8 +3656,8 @@ static int pidlist_array_load(struct cgroup *cgrp, enum cgroup_filetype type, if (!array) return -ENOMEM; /* now, populate the array */ - cgroup_task_iter_start(cgrp, &it); - while ((tsk = cgroup_task_iter_next(&it))) { + css_task_iter_start(&cgrp->dummy_css, &it); + while ((tsk = css_task_iter_next(&it))) { if (unlikely(n == length)) break; /* get tgid or pid for procs or tasks file respectively */ @@ -3666,7 +3668,7 @@ static int pidlist_array_load(struct cgroup *cgrp, enum cgroup_filetype type, if (pid > 0) /* make sure to only use valid results */ array[n++] = pid; } - cgroup_task_iter_end(&it); + css_task_iter_end(&it); length = n; /* now sort & (if procs) strip out duplicates */ sort(array, length, sizeof(pid_t), cmppid, NULL); @@ -3700,7 +3702,7 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry) { int ret = -EINVAL; struct cgroup *cgrp; - struct cgroup_task_iter it; + struct css_task_iter it; struct task_struct *tsk; /* @@ -3714,8 +3716,8 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry) ret = 0; cgrp = dentry->d_fsdata; - cgroup_task_iter_start(cgrp, &it); - while ((tsk = cgroup_task_iter_next(&it))) { + css_task_iter_start(&cgrp->dummy_css, &it); + while ((tsk = css_task_iter_next(&it))) { switch (tsk->state) { case TASK_RUNNING: stats->nr_running++; @@ -3735,7 +3737,7 @@ int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry) break; } } - cgroup_task_iter_end(&it); + css_task_iter_end(&it); err: return ret; diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index e0ab9bfd679a..5cd2b6d55243 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -258,7 +258,7 @@ static void update_if_frozen(struct cgroup_subsys_state *css) { struct freezer *freezer = css_freezer(css); struct cgroup_subsys_state *pos; - struct cgroup_task_iter it; + struct css_task_iter it; struct task_struct *task; WARN_ON_ONCE(!rcu_read_lock_held()); @@ -279,9 +279,9 @@ static void update_if_frozen(struct cgroup_subsys_state *css) } /* are all tasks frozen? */ - cgroup_task_iter_start(css->cgroup, &it); + css_task_iter_start(css, &it); - while ((task = cgroup_task_iter_next(&it))) { + while ((task = css_task_iter_next(&it))) { if (freezing(task)) { /* * freezer_should_skip() indicates that the task @@ -296,7 +296,7 @@ static void update_if_frozen(struct cgroup_subsys_state *css) freezer->state |= CGROUP_FROZEN; out_iter_end: - cgroup_task_iter_end(&it); + css_task_iter_end(&it); out_unlock: spin_unlock_irq(&freezer->lock); } @@ -322,26 +322,24 @@ static int freezer_read(struct cgroup_subsys_state *css, struct cftype *cft, static void freeze_cgroup(struct freezer *freezer) { - struct cgroup *cgroup = freezer->css.cgroup; - struct cgroup_task_iter it; + struct css_task_iter it; struct task_struct *task; - cgroup_task_iter_start(cgroup, &it); - while ((task = cgroup_task_iter_next(&it))) + css_task_iter_start(&freezer->css, &it); + while ((task = css_task_iter_next(&it))) freeze_task(task); - cgroup_task_iter_end(&it); + css_task_iter_end(&it); } static void unfreeze_cgroup(struct freezer *freezer) { - struct cgroup *cgroup = freezer->css.cgroup; - struct cgroup_task_iter it; + struct css_task_iter it; struct task_struct *task; - cgroup_task_iter_start(cgroup, &it); - while ((task = cgroup_task_iter_next(&it))) + css_task_iter_start(&freezer->css, &it); + while ((task = css_task_iter_next(&it))) __thaw_task(task); - cgroup_task_iter_end(&it); + css_task_iter_end(&it); } /** diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 6fe23f2ac742..39e52175f4af 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -832,8 +832,8 @@ static struct cpuset *effective_nodemask_cpuset(struct cpuset *cs) * @tsk: task to test * @data: cpuset to @tsk belongs to * - * Called by cgroup_scan_tasks() for each task in a cgroup whose - * cpus_allowed mask needs to be changed. + * Called by css_scan_tasks() for each task in a cgroup whose cpus_allowed + * mask needs to be changed. * * We don't need to re-check for the cgroup/cpuset membership, since we're * holding cpuset_mutex at this point. @@ -849,27 +849,26 @@ static void cpuset_change_cpumask(struct task_struct *tsk, void *data) /** * update_tasks_cpumask - Update the cpumasks of tasks in the cpuset. * @cs: the cpuset in which each task's cpus_allowed mask needs to be changed - * @heap: if NULL, defer allocating heap memory to cgroup_scan_tasks() + * @heap: if NULL, defer allocating heap memory to css_scan_tasks() * * Called with cpuset_mutex held * - * The cgroup_scan_tasks() function will scan all the tasks in a cgroup, + * The css_scan_tasks() function will scan all the tasks in a cgroup, * calling callback functions for each. * - * No return value. It's guaranteed that cgroup_scan_tasks() always returns 0 + * No return value. It's guaranteed that css_scan_tasks() always returns 0 * if @heap != NULL. */ static void update_tasks_cpumask(struct cpuset *cs, struct ptr_heap *heap) { - cgroup_scan_tasks(cs->css.cgroup, NULL, cpuset_change_cpumask, cs, - heap); + css_scan_tasks(&cs->css, NULL, cpuset_change_cpumask, cs, heap); } /* * update_tasks_cpumask_hier - Update the cpumasks of tasks in the hierarchy. * @root_cs: the root cpuset of the hierarchy * @update_root: update root cpuset or not? - * @heap: the heap used by cgroup_scan_tasks() + * @heap: the heap used by css_scan_tasks() * * This will update cpumasks of tasks in @root_cs and all other empty cpusets * which take on cpumask of @root_cs. @@ -1082,11 +1081,10 @@ static void *cpuset_being_rebound; /** * update_tasks_nodemask - Update the nodemasks of tasks in the cpuset. * @cs: the cpuset in which each task's mems_allowed mask needs to be changed - * @heap: if NULL, defer allocating heap memory to cgroup_scan_tasks() + * @heap: if NULL, defer allocating heap memory to css_scan_tasks() * - * Called with cpuset_mutex held - * No return value. It's guaranteed that cgroup_scan_tasks() always returns 0 - * if @heap != NULL. + * Called with cpuset_mutex held. No return value. It's guaranteed that + * css_scan_tasks() always returns 0 if @heap != NULL. */ static void update_tasks_nodemask(struct cpuset *cs, struct ptr_heap *heap) { @@ -1109,8 +1107,7 @@ static void update_tasks_nodemask(struct cpuset *cs, struct ptr_heap *heap) * It's ok if we rebind the same mm twice; mpol_rebind_mm() * is idempotent. Also migrate pages in each mm to new nodes. */ - cgroup_scan_tasks(cs->css.cgroup, NULL, cpuset_change_nodemask, &arg, - heap); + css_scan_tasks(&cs->css, NULL, cpuset_change_nodemask, &arg, heap); /* * All the tasks' nodemasks have been updated, update @@ -1126,7 +1123,7 @@ static void update_tasks_nodemask(struct cpuset *cs, struct ptr_heap *heap) * update_tasks_nodemask_hier - Update the nodemasks of tasks in the hierarchy. * @cs: the root cpuset of the hierarchy * @update_root: update the root cpuset or not? - * @heap: the heap used by cgroup_scan_tasks() + * @heap: the heap used by css_scan_tasks() * * This will update nodemasks of tasks in @root_cs and all other empty cpusets * which take on nodemask of @root_cs. @@ -1254,12 +1251,12 @@ static int update_relax_domain_level(struct cpuset *cs, s64 val) return 0; } -/* +/** * cpuset_change_flag - make a task's spread flags the same as its cpuset's * @tsk: task to be updated * @data: cpuset to @tsk belongs to * - * Called by cgroup_scan_tasks() for each task in a cgroup. + * Called by css_scan_tasks() for each task in a cgroup. * * We don't need to re-check for the cgroup/cpuset membership, since we're * holding cpuset_mutex at this point. @@ -1271,22 +1268,22 @@ static void cpuset_change_flag(struct task_struct *tsk, void *data) cpuset_update_task_spread_flag(cs, tsk); } -/* +/** * update_tasks_flags - update the spread flags of tasks in the cpuset. * @cs: the cpuset in which each task's spread flags needs to be changed - * @heap: if NULL, defer allocating heap memory to cgroup_scan_tasks() + * @heap: if NULL, defer allocating heap memory to css_scan_tasks() * * Called with cpuset_mutex held * - * The cgroup_scan_tasks() function will scan all the tasks in a cgroup, + * The css_scan_tasks() function will scan all the tasks in a cgroup, * calling callback functions for each. * - * No return value. It's guaranteed that cgroup_scan_tasks() always returns 0 + * No return value. It's guaranteed that css_scan_tasks() always returns 0 * if @heap != NULL. */ static void update_tasks_flags(struct cpuset *cs, struct ptr_heap *heap) { - cgroup_scan_tasks(cs->css.cgroup, NULL, cpuset_change_flag, cs, heap); + css_scan_tasks(&cs->css, NULL, cpuset_change_flag, cs, heap); } /* diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 5a5f4dc649f0..95106a993777 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1799,12 +1799,11 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL); totalpages = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1; for_each_mem_cgroup_tree(iter, memcg) { - struct cgroup *cgroup = iter->css.cgroup; - struct cgroup_task_iter it; + struct css_task_iter it; struct task_struct *task; - cgroup_task_iter_start(cgroup, &it); - while ((task = cgroup_task_iter_next(&it))) { + css_task_iter_start(&iter->css, &it); + while ((task = css_task_iter_next(&it))) { switch (oom_scan_process_thread(task, totalpages, NULL, false)) { case OOM_SCAN_SELECT: @@ -1817,7 +1816,7 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, case OOM_SCAN_CONTINUE: continue; case OOM_SCAN_ABORT: - cgroup_task_iter_end(&it); + css_task_iter_end(&it); mem_cgroup_iter_break(memcg, iter); if (chosen) put_task_struct(chosen); @@ -1834,7 +1833,7 @@ static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask, get_task_struct(chosen); } } - cgroup_task_iter_end(&it); + css_task_iter_end(&it); } if (!chosen) -- cgit v1.2.3-71-gd317 From 81eeaf0411204f52af8ef78ff107cfca2fcfec1d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:26 -0400 Subject: cgroup: make cftype->[un]register_event() deal with cgroup_subsys_state instead of cgroup cgroup is in the process of converting to css (cgroup_subsys_state) from cgroup as the principal subsystem interface handle. This is mostly to prepare for the unified hierarchy support where css's will be created and destroyed dynamically but also helps cleaning up subsystem implementations as css is usually what they are interested in anyway. cftype->[un]register_event() is among the remaining couple interfaces which still use struct cgroup. Convert it to cgroup_subsys_state. The conversion is mostly mechanical and removes the last users of mem_cgroup_from_cont() and cg_to_vmpressure(), which are removed. v2: indentation update as suggested by Li Zefan. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Michal Hocko Cc: Johannes Weiner Cc: Balbir Singh --- include/linux/cgroup.h | 10 ++++++---- include/linux/vmpressure.h | 6 ++++-- kernel/cgroup.c | 15 ++++++++------- mm/memcontrol.c | 21 ++++++++------------- mm/vmpressure.c | 21 +++++++++------------ 5 files changed, 35 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index cd105fce089c..b065d24486e6 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -506,16 +506,18 @@ struct cftype { * you want to provide this functionality. Use eventfd_signal() * on eventfd to send notification to userspace. */ - int (*register_event)(struct cgroup *cgrp, struct cftype *cft, - struct eventfd_ctx *eventfd, const char *args); + int (*register_event)(struct cgroup_subsys_state *css, + struct cftype *cft, struct eventfd_ctx *eventfd, + const char *args); /* * unregister_event() callback will be called when userspace * closes the eventfd or on cgroup removing. * This callback must be implemented, if you want provide * notification functionality. */ - void (*unregister_event)(struct cgroup *cgrp, struct cftype *cft, - struct eventfd_ctx *eventfd); + void (*unregister_event)(struct cgroup_subsys_state *css, + struct cftype *cft, + struct eventfd_ctx *eventfd); }; /* diff --git a/include/linux/vmpressure.h b/include/linux/vmpressure.h index 76be077340ea..b239482bd39d 100644 --- a/include/linux/vmpressure.h +++ b/include/linux/vmpressure.h @@ -33,10 +33,12 @@ extern void vmpressure_init(struct vmpressure *vmpr); extern struct vmpressure *memcg_to_vmpressure(struct mem_cgroup *memcg); extern struct cgroup_subsys_state *vmpressure_to_css(struct vmpressure *vmpr); extern struct vmpressure *css_to_vmpressure(struct cgroup_subsys_state *css); -extern int vmpressure_register_event(struct cgroup *cg, struct cftype *cft, +extern int vmpressure_register_event(struct cgroup_subsys_state *css, + struct cftype *cft, struct eventfd_ctx *eventfd, const char *args); -extern void vmpressure_unregister_event(struct cgroup *cg, struct cftype *cft, +extern void vmpressure_unregister_event(struct cgroup_subsys_state *css, + struct cftype *cft, struct eventfd_ctx *eventfd); #else static inline void vmpressure(gfp_t gfp, struct mem_cgroup *memcg, diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 8c57301d0561..a71f2e0f9711 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -159,9 +159,9 @@ struct css_id { */ struct cgroup_event { /* - * Cgroup which the event belongs to. + * css which the event belongs to. */ - struct cgroup *cgrp; + struct cgroup_subsys_state *css; /* * Control file which the event associated. */ @@ -3955,11 +3955,12 @@ static void cgroup_event_remove(struct work_struct *work) { struct cgroup_event *event = container_of(work, struct cgroup_event, remove); - struct cgroup *cgrp = event->cgrp; + struct cgroup_subsys_state *css = event->css; + struct cgroup *cgrp = css->cgroup; remove_wait_queue(event->wqh, &event->wait); - event->cft->unregister_event(cgrp, event->cft, event->eventfd); + event->cft->unregister_event(css, event->cft, event->eventfd); /* Notify userspace the event is going away. */ eventfd_signal(event->eventfd, 1); @@ -3979,7 +3980,7 @@ static int cgroup_event_wake(wait_queue_t *wait, unsigned mode, { struct cgroup_event *event = container_of(wait, struct cgroup_event, wait); - struct cgroup *cgrp = event->cgrp; + struct cgroup *cgrp = event->css->cgroup; unsigned long flags = (unsigned long)key; if (flags & POLLHUP) { @@ -4048,7 +4049,7 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *css, event = kzalloc(sizeof(*event), GFP_KERNEL); if (!event) return -ENOMEM; - event->cgrp = cgrp; + event->css = css; INIT_LIST_HEAD(&event->list); init_poll_funcptr(&event->pt, cgroup_event_ptable_queue_proc); init_waitqueue_func_entry(&event->wait, cgroup_event_wake); @@ -4099,7 +4100,7 @@ static int cgroup_write_event_control(struct cgroup_subsys_state *css, goto out_put_cfile; } - ret = event->cft->register_event(cgrp, event->cft, + ret = event->cft->register_event(css, event->cft, event->eventfd, buffer); if (ret) goto out_put_cfile; diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 95106a993777..2885e3e85047 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1034,11 +1034,6 @@ static void memcg_check_events(struct mem_cgroup *memcg, struct page *page) preempt_enable(); } -static inline struct mem_cgroup *mem_cgroup_from_cont(struct cgroup *cont) -{ - return mem_cgroup_from_css(cgroup_css(cont, mem_cgroup_subsys_id)); -} - struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p) { /* @@ -5620,10 +5615,10 @@ static void mem_cgroup_oom_notify(struct mem_cgroup *memcg) mem_cgroup_oom_notify_cb(iter); } -static int mem_cgroup_usage_register_event(struct cgroup *cgrp, +static int mem_cgroup_usage_register_event(struct cgroup_subsys_state *css, struct cftype *cft, struct eventfd_ctx *eventfd, const char *args) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); struct mem_cgroup_thresholds *thresholds; struct mem_cgroup_threshold_ary *new; enum res_type type = MEMFILE_TYPE(cft->private); @@ -5703,10 +5698,10 @@ unlock: return ret; } -static void mem_cgroup_usage_unregister_event(struct cgroup *cgrp, +static void mem_cgroup_usage_unregister_event(struct cgroup_subsys_state *css, struct cftype *cft, struct eventfd_ctx *eventfd) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); struct mem_cgroup_thresholds *thresholds; struct mem_cgroup_threshold_ary *new; enum res_type type = MEMFILE_TYPE(cft->private); @@ -5782,10 +5777,10 @@ unlock: mutex_unlock(&memcg->thresholds_lock); } -static int mem_cgroup_oom_register_event(struct cgroup *cgrp, +static int mem_cgroup_oom_register_event(struct cgroup_subsys_state *css, struct cftype *cft, struct eventfd_ctx *eventfd, const char *args) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); struct mem_cgroup_eventfd_list *event; enum res_type type = MEMFILE_TYPE(cft->private); @@ -5807,10 +5802,10 @@ static int mem_cgroup_oom_register_event(struct cgroup *cgrp, return 0; } -static void mem_cgroup_oom_unregister_event(struct cgroup *cgrp, +static void mem_cgroup_oom_unregister_event(struct cgroup_subsys_state *css, struct cftype *cft, struct eventfd_ctx *eventfd) { - struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); + struct mem_cgroup *memcg = mem_cgroup_from_css(css); struct mem_cgroup_eventfd_list *ev, *tmp; enum res_type type = MEMFILE_TYPE(cft->private); diff --git a/mm/vmpressure.c b/mm/vmpressure.c index 2a8a736e95cc..13489b1f4ee3 100644 --- a/mm/vmpressure.c +++ b/mm/vmpressure.c @@ -74,11 +74,6 @@ static struct vmpressure *work_to_vmpressure(struct work_struct *work) return container_of(work, struct vmpressure, work); } -static struct vmpressure *cg_to_vmpressure(struct cgroup *cg) -{ - return css_to_vmpressure(cgroup_css(cg, mem_cgroup_subsys_id)); -} - static struct vmpressure *vmpressure_parent(struct vmpressure *vmpr) { struct cgroup_subsys_state *css = vmpressure_to_css(vmpr); @@ -283,7 +278,7 @@ void vmpressure_prio(gfp_t gfp, struct mem_cgroup *memcg, int prio) /** * vmpressure_register_event() - Bind vmpressure notifications to an eventfd - * @cg: cgroup that is interested in vmpressure notifications + * @css: css that is interested in vmpressure notifications * @cft: cgroup control files handle * @eventfd: eventfd context to link notifications with * @args: event arguments (used to set up a pressure level threshold) @@ -298,10 +293,11 @@ void vmpressure_prio(gfp_t gfp, struct mem_cgroup *memcg, int prio) * cftype).register_event, and then cgroup core will handle everything by * itself. */ -int vmpressure_register_event(struct cgroup *cg, struct cftype *cft, - struct eventfd_ctx *eventfd, const char *args) +int vmpressure_register_event(struct cgroup_subsys_state *css, + struct cftype *cft, struct eventfd_ctx *eventfd, + const char *args) { - struct vmpressure *vmpr = cg_to_vmpressure(cg); + struct vmpressure *vmpr = css_to_vmpressure(css); struct vmpressure_event *ev; int level; @@ -329,7 +325,7 @@ int vmpressure_register_event(struct cgroup *cg, struct cftype *cft, /** * vmpressure_unregister_event() - Unbind eventfd from vmpressure - * @cg: cgroup handle + * @css: css handle * @cft: cgroup control files handle * @eventfd: eventfd context that was used to link vmpressure with the @cg * @@ -341,10 +337,11 @@ int vmpressure_register_event(struct cgroup *cg, struct cftype *cft, * cftype).unregister_event, and then cgroup core will handle everything * by itself. */ -void vmpressure_unregister_event(struct cgroup *cg, struct cftype *cft, +void vmpressure_unregister_event(struct cgroup_subsys_state *css, + struct cftype *cft, struct eventfd_ctx *eventfd) { - struct vmpressure *vmpr = cg_to_vmpressure(cg); + struct vmpressure *vmpr = css_to_vmpressure(css); struct vmpressure_event *ev; mutex_lock(&vmpr->events_lock); -- cgit v1.2.3-71-gd317 From d99c8727e7bbc01b70e2c57e6127bfab26b868fd Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:27 -0400 Subject: cgroup: make cgroup_taskset deal with cgroup_subsys_state instead of cgroup cgroup is in the process of converting to css (cgroup_subsys_state) from cgroup as the principal subsystem interface handle. This is mostly to prepare for the unified hierarchy support where css's will be created and destroyed dynamically but also helps cleaning up subsystem implementations as css is usually what they are interested in anyway. cgroup_taskset which is used by the subsystem attach methods is the last cgroup subsystem API which isn't using css as the handle. Update cgroup_taskset_cur_cgroup() to cgroup_taskset_cur_css() and cgroup_taskset_for_each() to take @skip_css instead of @skip_cgrp. The conversions are pretty mechanical. One exception is cpuset::cgroup_cs(), which lost its last user and got removed. This patch shouldn't introduce any functional changes. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Daniel Wagner Cc: Ingo Molnar Cc: Matt Helsley Cc: Steven Rostedt --- block/blk-cgroup.c | 2 +- include/linux/cgroup.h | 12 +++++++----- kernel/cgroup.c | 16 +++++++++------- kernel/cgroup_freezer.c | 2 +- kernel/cpuset.c | 15 +++++---------- kernel/events/core.c | 2 +- kernel/sched/core.c | 4 ++-- net/core/netprio_cgroup.c | 2 +- net/sched/cls_cgroup.c | 2 +- 9 files changed, 28 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 4b40640240a4..54ad00292edf 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -891,7 +891,7 @@ static int blkcg_can_attach(struct cgroup_subsys_state *css, int ret = 0; /* task_lock() is needed to avoid races with exit_io_context() */ - cgroup_taskset_for_each(task, css->cgroup, tset) { + cgroup_taskset_for_each(task, css, tset) { task_lock(task); ioc = task->io_context; if (ioc && atomic_read(&ioc->nr_tasks) > 1) diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index b065d24486e6..d9a970568be9 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -562,20 +562,22 @@ int cgroup_task_count(const struct cgroup *cgrp); struct cgroup_taskset; struct task_struct *cgroup_taskset_first(struct cgroup_taskset *tset); struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset); -struct cgroup *cgroup_taskset_cur_cgroup(struct cgroup_taskset *tset); +struct cgroup_subsys_state *cgroup_taskset_cur_css(struct cgroup_taskset *tset, + int subsys_id); int cgroup_taskset_size(struct cgroup_taskset *tset); /** * cgroup_taskset_for_each - iterate cgroup_taskset * @task: the loop cursor - * @skip_cgrp: skip if task's cgroup matches this, %NULL to iterate through all + * @skip_css: skip if task's css matches this, %NULL to iterate through all * @tset: taskset to iterate */ -#define cgroup_taskset_for_each(task, skip_cgrp, tset) \ +#define cgroup_taskset_for_each(task, skip_css, tset) \ for ((task) = cgroup_taskset_first((tset)); (task); \ (task) = cgroup_taskset_next((tset))) \ - if (!(skip_cgrp) || \ - cgroup_taskset_cur_cgroup((tset)) != (skip_cgrp)) + if (!(skip_css) || \ + cgroup_taskset_cur_css((tset), \ + (skip_css)->ss->subsys_id) != (skip_css)) /* * Control Group subsystem type. diff --git a/kernel/cgroup.c b/kernel/cgroup.c index a71f2e0f9711..e5bfb2a81dcb 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -1907,18 +1907,20 @@ struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset) EXPORT_SYMBOL_GPL(cgroup_taskset_next); /** - * cgroup_taskset_cur_cgroup - return the matching cgroup for the current task + * cgroup_taskset_cur_css - return the matching css for the current task * @tset: taskset of interest + * @subsys_id: the ID of the target subsystem * - * Return the cgroup for the current (last returned) task of @tset. This - * function must be preceded by either cgroup_taskset_first() or - * cgroup_taskset_next(). + * Return the css for the current (last returned) task of @tset for + * subsystem specified by @subsys_id. This function must be preceded by + * either cgroup_taskset_first() or cgroup_taskset_next(). */ -struct cgroup *cgroup_taskset_cur_cgroup(struct cgroup_taskset *tset) +struct cgroup_subsys_state *cgroup_taskset_cur_css(struct cgroup_taskset *tset, + int subsys_id) { - return tset->cur_cgrp; + return cgroup_css(tset->cur_cgrp, subsys_id); } -EXPORT_SYMBOL_GPL(cgroup_taskset_cur_cgroup); +EXPORT_SYMBOL_GPL(cgroup_taskset_cur_css); /** * cgroup_taskset_size - return the number of tasks in taskset diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 5cd2b6d55243..224da9aa27f5 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -189,7 +189,7 @@ static void freezer_attach(struct cgroup_subsys_state *new_css, * current state before executing the following - !frozen tasks may * be visible in a FROZEN cgroup and frozen tasks in a THAWED one. */ - cgroup_taskset_for_each(task, new_css->cgroup, tset) { + cgroup_taskset_for_each(task, new_css, tset) { if (!(freezer->state & CGROUP_FREEZING)) { __thaw_task(task); } else { diff --git a/kernel/cpuset.c b/kernel/cpuset.c index 39e52175f4af..bf69717325b4 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -119,12 +119,6 @@ static inline struct cpuset *css_cs(struct cgroup_subsys_state *css) return css ? container_of(css, struct cpuset, css) : NULL; } -/* Retrieve the cpuset for a cgroup */ -static inline struct cpuset *cgroup_cs(struct cgroup *cgrp) -{ - return css_cs(cgroup_css(cgrp, cpuset_subsys_id)); -} - /* Retrieve the cpuset for a task */ static inline struct cpuset *task_cs(struct task_struct *task) { @@ -1459,7 +1453,7 @@ static int cpuset_can_attach(struct cgroup_subsys_state *css, (cpumask_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed))) goto out_unlock; - cgroup_taskset_for_each(task, css->cgroup, tset) { + cgroup_taskset_for_each(task, css, tset) { /* * Kthreads which disallow setaffinity shouldn't be moved * to a new cpuset; we don't want to change their cpu @@ -1511,9 +1505,10 @@ static void cpuset_attach(struct cgroup_subsys_state *css, struct mm_struct *mm; struct task_struct *task; struct task_struct *leader = cgroup_taskset_first(tset); - struct cgroup *oldcgrp = cgroup_taskset_cur_cgroup(tset); + struct cgroup_subsys_state *oldcss = cgroup_taskset_cur_css(tset, + cpuset_subsys_id); struct cpuset *cs = css_cs(css); - struct cpuset *oldcs = cgroup_cs(oldcgrp); + struct cpuset *oldcs = css_cs(oldcss); struct cpuset *cpus_cs = effective_cpumask_cpuset(cs); struct cpuset *mems_cs = effective_nodemask_cpuset(cs); @@ -1527,7 +1522,7 @@ static void cpuset_attach(struct cgroup_subsys_state *css, guarantee_online_mems(mems_cs, &cpuset_attach_nodemask_to); - cgroup_taskset_for_each(task, css->cgroup, tset) { + cgroup_taskset_for_each(task, css, tset) { /* * can_attach beforehand should guarantee that this doesn't * fail. TODO: have a better way to handle failure here diff --git a/kernel/events/core.c b/kernel/events/core.c index 9705a0ed1dce..c199c4f24910 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7816,7 +7816,7 @@ static void perf_cgroup_attach(struct cgroup_subsys_state *css, { struct task_struct *task; - cgroup_taskset_for_each(task, css->cgroup, tset) + cgroup_taskset_for_each(task, css, tset) task_function_call(task, __perf_cgroup_move, task); } diff --git a/kernel/sched/core.c b/kernel/sched/core.c index cc9a49266382..a7122d5b8310 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7135,7 +7135,7 @@ static int cpu_cgroup_can_attach(struct cgroup_subsys_state *css, { struct task_struct *task; - cgroup_taskset_for_each(task, css->cgroup, tset) { + cgroup_taskset_for_each(task, css, tset) { #ifdef CONFIG_RT_GROUP_SCHED if (!sched_rt_can_attach(css_tg(css), task)) return -EINVAL; @@ -7153,7 +7153,7 @@ static void cpu_cgroup_attach(struct cgroup_subsys_state *css, { struct task_struct *task; - cgroup_taskset_for_each(task, css->cgroup, tset) + cgroup_taskset_for_each(task, css, tset) sched_move_task(task); } diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index e00f60e5baea..d9cd627e6a16 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -224,7 +224,7 @@ static void net_prio_attach(struct cgroup_subsys_state *css, struct task_struct *p; void *v; - cgroup_taskset_for_each(p, css->cgroup, tset) { + cgroup_taskset_for_each(p, css, tset) { task_lock(p); v = (void *)(unsigned long)task_netprioidx(p); iterate_fd(p->files, 0, update_netprio, v); diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 8ea1184cec92..867b4a3e3980 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -74,7 +74,7 @@ static void cgrp_attach(struct cgroup_subsys_state *css, struct task_struct *p; void *v; - cgroup_taskset_for_each(p, css->cgroup, tset) { + cgroup_taskset_for_each(p, css, tset) { task_lock(p); v = (void *)(unsigned long)task_cls_classid(p); iterate_fd(p->files, 0, update_classid, v); -- cgit v1.2.3-71-gd317 From 95109b627ba6a043c181fa5fa45d1c754dd44fbc Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:27 -0400 Subject: cgroup: unexport cgroup_css() cgroup_css() no longer has any user left outside cgroup.c proper and we don't want subsystems to grow new usages of the function. cgroup core should always provide the css to use to the subsystems, which will make dynamic creation and destruction of css's across the lifetime of a cgroup much more manageable than exposing the cgroup directly to subsystems and let them dereference css's from it. Make cgroup_css() a static function in cgroup.c. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 13 ------------- kernel/cgroup.c | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index d9a970568be9..c40e508d54e9 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -677,19 +677,6 @@ struct cgroup_subsys_state *css_parent(struct cgroup_subsys_state *css) return &parent_cgrp->dummy_css; } -/** - * cgroup_css - obtain a cgroup's css for the specified subsystem - * @cgrp: the cgroup of interest - * @subsys_id: the subsystem of interest - * - * Return @cgrp's css (cgroup_subsys_state) associated with @subsys_id. - */ -static inline struct cgroup_subsys_state *cgroup_css(struct cgroup *cgrp, - int subsys_id) -{ - return cgrp->subsys[subsys_id]; -} - /** * task_css_set_check - obtain a task's css_set with extra access conditions * @task: the task to obtain css_set for diff --git a/kernel/cgroup.c b/kernel/cgroup.c index e5bfb2a81dcb..c02a288a4e3d 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -222,6 +222,19 @@ static int cgroup_destroy_locked(struct cgroup *cgrp); static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], bool is_add); +/** + * cgroup_css - obtain a cgroup's css for the specified subsystem + * @cgrp: the cgroup of interest + * @subsys_id: the subsystem of interest + * + * Return @cgrp's css (cgroup_subsys_state) associated with @subsys_id. + */ +static struct cgroup_subsys_state *cgroup_css(struct cgroup *cgrp, + int subsys_id) +{ + return cgrp->subsys[subsys_id]; +} + /* convenient tests for these bits */ static inline bool cgroup_is_dead(const struct cgroup *cgrp) { -- cgit v1.2.3-71-gd317 From bd8815a6d802fc16a7a106e170593aa05dc17e72 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 8 Aug 2013 20:11:27 -0400 Subject: cgroup: make css_for_each_descendant() and friends include the origin css in the iteration Previously, all css descendant iterators didn't include the origin (root of subtree) css in the iteration. The reasons were maintaining consistency with css_for_each_child() and that at the time of introduction more use cases needed skipping the origin anyway; however, given that css_is_descendant() considers self to be a descendant, omitting the origin css has become more confusing and looking at the accumulated use cases rather clearly indicates that including origin would result in simpler code overall. While this is a change which can easily lead to subtle bugs, cgroup API including the iterators has recently gone through major restructuring and no out-of-tree changes will be applicable without adjustments making this a relatively acceptable opportunity for this type of change. The conversions are mostly straight-forward. If the iteration block had explicit origin handling before or after, it's moved inside the iteration. If not, if (pos == origin) continue; is added. Some conversions add extra reference get/put around origin handling by consolidating origin handling and the rest. While the extra ref operations aren't strictly necessary, this shouldn't cause any noticeable difference. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Vivek Goyal Acked-by: Aristeu Rozanski Acked-by: Michal Hocko Cc: Jens Axboe Cc: Matt Helsley Cc: Johannes Weiner Cc: Balbir Singh --- block/blk-cgroup.c | 8 ++------ block/blk-cgroup.h | 4 +++- block/blk-throttle.c | 3 --- include/linux/cgroup.h | 17 +++++++++-------- kernel/cgroup.c | 29 +++++++++++------------------ kernel/cgroup_freezer.c | 29 ++++++++++++++++------------- kernel/cpuset.c | 42 ++++++++++++++++++++++++++---------------- mm/memcontrol.c | 9 +-------- security/device_cgroup.c | 2 +- 9 files changed, 69 insertions(+), 74 deletions(-) (limited to 'include') diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 54ad00292edf..e90c7c164c83 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -615,12 +615,10 @@ u64 blkg_stat_recursive_sum(struct blkg_policy_data *pd, int off) struct blkcg_policy *pol = blkcg_policy[pd->plid]; struct blkcg_gq *pos_blkg; struct cgroup_subsys_state *pos_css; - u64 sum; + u64 sum = 0; lockdep_assert_held(pd->blkg->q->queue_lock); - sum = blkg_stat_read((void *)pd + off); - rcu_read_lock(); blkg_for_each_descendant_pre(pos_blkg, pos_css, pd_to_blkg(pd)) { struct blkg_policy_data *pos_pd = blkg_to_pd(pos_blkg, pol); @@ -650,13 +648,11 @@ struct blkg_rwstat blkg_rwstat_recursive_sum(struct blkg_policy_data *pd, struct blkcg_policy *pol = blkcg_policy[pd->plid]; struct blkcg_gq *pos_blkg; struct cgroup_subsys_state *pos_css; - struct blkg_rwstat sum; + struct blkg_rwstat sum = { }; int i; lockdep_assert_held(pd->blkg->q->queue_lock); - sum = blkg_rwstat_read((void *)pd + off); - rcu_read_lock(); blkg_for_each_descendant_pre(pos_blkg, pos_css, pd_to_blkg(pd)) { struct blkg_policy_data *pos_pd = blkg_to_pd(pos_blkg, pol); diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index 855538630300..ae6969a7ffd4 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -291,6 +291,7 @@ struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q, * read locked. If called under either blkcg or queue lock, the iteration * is guaranteed to include all and only online blkgs. The caller may * update @pos_css by calling css_rightmost_descendant() to skip subtree. + * @p_blkg is included in the iteration and the first node to be visited. */ #define blkg_for_each_descendant_pre(d_blkg, pos_css, p_blkg) \ css_for_each_descendant_pre((pos_css), &(p_blkg)->blkcg->css) \ @@ -304,7 +305,8 @@ struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg, struct request_queue *q, * @p_blkg: target blkg to walk descendants of * * Similar to blkg_for_each_descendant_pre() but performs post-order - * traversal instead. Synchronization rules are the same. + * traversal instead. Synchronization rules are the same. @p_blkg is + * included in the iteration and the last node to be visited. */ #define blkg_for_each_descendant_post(d_blkg, pos_css, p_blkg) \ css_for_each_descendant_post((pos_css), &(p_blkg)->blkcg->css) \ diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 8cefa7f8590e..8331aba9426f 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -1379,7 +1379,6 @@ static int tg_set_conf(struct cgroup_subsys_state *css, struct cftype *cft, * restrictions in the whole hierarchy and allows them to bypass * blk-throttle. */ - tg_update_has_rules(tg); blkg_for_each_descendant_pre(blkg, pos_css, ctx.blkg) tg_update_has_rules(blkg_to_tg(blkg)); @@ -1639,8 +1638,6 @@ void blk_throtl_drain(struct request_queue *q) blkg_for_each_descendant_post(blkg, pos_css, td->queue->root_blkg) tg_drain_bios(&blkg_to_tg(blkg)->service_queue); - tg_drain_bios(&td_root_tg(td)->service_queue); - /* finally, transfer bios from top-level tg's into the td */ tg_drain_bios(&td->service_queue); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c40e508d54e9..8ec5b0f38292 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -798,7 +798,8 @@ css_rightmost_descendant(struct cgroup_subsys_state *pos); * @pos: the css * to use as the loop cursor * @root: css whose descendants to walk * - * Walk @root's descendants. Must be called under rcu_read_lock(). A + * Walk @root's descendants. @root is included in the iteration and the + * first node to be visited. Must be called under rcu_read_lock(). A * descendant css which hasn't finished ->css_online() or already has * finished ->css_offline() may show up during traversal and it's each * subsystem's responsibility to verify that each @pos is alive. @@ -820,13 +821,12 @@ css_rightmost_descendant(struct cgroup_subsys_state *pos); * * my_update_state(@css) * { - * Lock @css; - * Update @css's state; - * Unlock @css; - * * css_for_each_descendant_pre(@pos, @css) { * Lock @pos; - * Verify @pos is alive and inherit state from @pos's parent; + * if (@pos == @css) + * Update @css's state; + * else + * Verify @pos is alive and inherit state from its parent; * Unlock @pos; * } * } @@ -864,8 +864,9 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, * @css: css whose descendants to walk * * Similar to css_for_each_descendant_pre() but performs post-order - * traversal instead. Note that the walk visibility guarantee described in - * pre-order walk doesn't apply the same to post-order walks. + * traversal instead. @root is included in the iteration and the last + * node to be visited. Note that the walk visibility guarantee described + * in pre-order walk doesn't apply the same to post-order walks. */ #define css_for_each_descendant_post(pos, css) \ for ((pos) = css_next_descendant_post(NULL, (css)); (pos); \ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index c02a288a4e3d..52f0498db946 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2868,17 +2868,6 @@ static int cgroup_cfts_commit(struct cftype *cfts, bool is_add) mutex_unlock(&cgroup_mutex); - /* @root always needs to be updated */ - inode = root->dentry->d_inode; - mutex_lock(&inode->i_mutex); - mutex_lock(&cgroup_mutex); - ret = cgroup_addrm_files(root, cfts, is_add); - mutex_unlock(&cgroup_mutex); - mutex_unlock(&inode->i_mutex); - - if (ret) - goto out_deact; - /* add/rm files for all cgroups created before */ rcu_read_lock(); css_for_each_descendant_pre(css, cgroup_css(root, ss->subsys_id)) { @@ -2907,7 +2896,6 @@ static int cgroup_cfts_commit(struct cftype *cfts, bool is_add) } rcu_read_unlock(); dput(prev); -out_deact: deactivate_super(sb); return ret; } @@ -3099,7 +3087,8 @@ EXPORT_SYMBOL_GPL(css_next_child); * @root: css whose descendants to walk * * To be used by css_for_each_descendant_pre(). Find the next descendant - * to visit for pre-order traversal of @root's descendants. + * to visit for pre-order traversal of @root's descendants. @root is + * included in the iteration and the first node to be visited. * * While this function requires RCU read locking, it doesn't require the * whole traversal to be contained in a single RCU critical section. This @@ -3114,9 +3103,9 @@ css_next_descendant_pre(struct cgroup_subsys_state *pos, WARN_ON_ONCE(!rcu_read_lock_held()); - /* if first iteration, pretend we just visited @root */ + /* if first iteration, visit @root */ if (!pos) - pos = root; + return root; /* visit the first child if exists */ next = css_next_child(NULL, pos); @@ -3186,7 +3175,8 @@ css_leftmost_descendant(struct cgroup_subsys_state *pos) * @root: css whose descendants to walk * * To be used by css_for_each_descendant_post(). Find the next descendant - * to visit for post-order traversal of @root's descendants. + * to visit for post-order traversal of @root's descendants. @root is + * included in the iteration and the last node to be visited. * * While this function requires RCU read locking, it doesn't require the * whole traversal to be contained in a single RCU critical section. This @@ -3207,14 +3197,17 @@ css_next_descendant_post(struct cgroup_subsys_state *pos, return next != root ? next : NULL; } + /* if we visited @root, we're done */ + if (pos == root) + return NULL; + /* if there's an unvisited sibling, visit its leftmost descendant */ next = css_next_child(pos, css_parent(pos)); if (next) return css_leftmost_descendant(next); /* no sibling left, visit parent */ - next = css_parent(pos); - return next != root ? next : NULL; + return css_parent(pos); } EXPORT_SYMBOL_GPL(css_next_descendant_post); diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 224da9aa27f5..f0ff64d0ebaa 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -311,7 +311,6 @@ static int freezer_read(struct cgroup_subsys_state *css, struct cftype *cft, /* update states bottom-up */ css_for_each_descendant_post(pos, css) update_if_frozen(pos); - update_if_frozen(css); rcu_read_unlock(); @@ -391,11 +390,6 @@ static void freezer_change_state(struct freezer *freezer, bool freeze) { struct cgroup_subsys_state *pos; - /* update @freezer */ - spin_lock_irq(&freezer->lock); - freezer_apply_state(freezer, freeze, CGROUP_FREEZING_SELF); - spin_unlock_irq(&freezer->lock); - /* * Update all its descendants in pre-order traversal. Each * descendant will try to inherit its parent's FREEZING state as @@ -406,14 +400,23 @@ static void freezer_change_state(struct freezer *freezer, bool freeze) struct freezer *pos_f = css_freezer(pos); struct freezer *parent = parent_freezer(pos_f); - /* - * Our update to @parent->state is already visible which is - * all we need. No need to lock @parent. For more info on - * synchronization, see freezer_post_create(). - */ spin_lock_irq(&pos_f->lock); - freezer_apply_state(pos_f, parent->state & CGROUP_FREEZING, - CGROUP_FREEZING_PARENT); + + if (pos_f == freezer) { + freezer_apply_state(pos_f, freeze, + CGROUP_FREEZING_SELF); + } else { + /* + * Our update to @parent->state is already visible + * which is all we need. No need to lock @parent. + * For more info on synchronization, see + * freezer_post_create(). + */ + freezer_apply_state(pos_f, + parent->state & CGROUP_FREEZING, + CGROUP_FREEZING_PARENT); + } + spin_unlock_irq(&pos_f->lock); } rcu_read_unlock(); diff --git a/kernel/cpuset.c b/kernel/cpuset.c index bf69717325b4..72a0383f382f 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -222,7 +222,8 @@ static struct cpuset top_cpuset = { * * Walk @des_cs through the online descendants of @root_cs. Must be used * with RCU read locked. The caller may modify @pos_css by calling - * css_rightmost_descendant() to skip subtree. + * css_rightmost_descendant() to skip subtree. @root_cs is included in the + * iteration and the first node to be visited. */ #define cpuset_for_each_descendant_pre(des_cs, pos_css, root_cs) \ css_for_each_descendant_pre((pos_css), &(root_cs)->css) \ @@ -506,6 +507,9 @@ static void update_domain_attr_tree(struct sched_domain_attr *dattr, rcu_read_lock(); cpuset_for_each_descendant_pre(cp, pos_css, root_cs) { + if (cp == root_cs) + continue; + /* skip the whole subtree if @cp doesn't have any CPU */ if (cpumask_empty(cp->cpus_allowed)) { pos_css = css_rightmost_descendant(pos_css); @@ -613,6 +617,8 @@ static int generate_sched_domains(cpumask_var_t **domains, rcu_read_lock(); cpuset_for_each_descendant_pre(cp, pos_css, &top_cpuset) { + if (cp == &top_cpuset) + continue; /* * Continue traversing beyond @cp iff @cp has some CPUs and * isn't load balancing. The former is obvious. The @@ -875,15 +881,17 @@ static void update_tasks_cpumask_hier(struct cpuset *root_cs, struct cpuset *cp; struct cgroup_subsys_state *pos_css; - if (update_root) - update_tasks_cpumask(root_cs, heap); - rcu_read_lock(); cpuset_for_each_descendant_pre(cp, pos_css, root_cs) { - /* skip the whole subtree if @cp have some CPU */ - if (!cpumask_empty(cp->cpus_allowed)) { - pos_css = css_rightmost_descendant(pos_css); - continue; + if (cp == root_cs) { + if (!update_root) + continue; + } else { + /* skip the whole subtree if @cp have some CPU */ + if (!cpumask_empty(cp->cpus_allowed)) { + pos_css = css_rightmost_descendant(pos_css); + continue; + } } if (!css_tryget(&cp->css)) continue; @@ -1130,15 +1138,17 @@ static void update_tasks_nodemask_hier(struct cpuset *root_cs, struct cpuset *cp; struct cgroup_subsys_state *pos_css; - if (update_root) - update_tasks_nodemask(root_cs, heap); - rcu_read_lock(); cpuset_for_each_descendant_pre(cp, pos_css, root_cs) { - /* skip the whole subtree if @cp have some CPU */ - if (!nodes_empty(cp->mems_allowed)) { - pos_css = css_rightmost_descendant(pos_css); - continue; + if (cp == root_cs) { + if (!update_root) + continue; + } else { + /* skip the whole subtree if @cp have some CPU */ + if (!nodes_empty(cp->mems_allowed)) { + pos_css = css_rightmost_descendant(pos_css); + continue; + } } if (!css_tryget(&cp->css)) continue; @@ -2237,7 +2247,7 @@ static void cpuset_hotplug_workfn(struct work_struct *work) rcu_read_lock(); cpuset_for_each_descendant_pre(cs, pos_css, &top_cpuset) { - if (!css_tryget(&cs->css)) + if (cs == &top_cpuset || !css_tryget(&cs->css)) continue; rcu_read_unlock(); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 2885e3e85047..b89d4cbc0c08 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1079,14 +1079,7 @@ static struct mem_cgroup *__mem_cgroup_iter_next(struct mem_cgroup *root, { struct cgroup_subsys_state *prev_css, *next_css; - /* - * Root is not visited by cgroup iterators so it needs an - * explicit visit. - */ - if (!last_visited) - return root; - - prev_css = (last_visited == root) ? NULL : &last_visited->css; + prev_css = last_visited ? &last_visited->css : NULL; skip_node: next_css = css_next_descendant_pre(prev_css, &root->css); diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 9bf230aa28b0..c123628d3f84 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -456,7 +456,7 @@ static int propagate_exception(struct dev_cgroup *devcg_root, * methods), and online ones are safe to access outside RCU * read lock without bumping refcnt. */ - if (!is_devcg_online(devcg)) + if (pos == &devcg_root->css || !is_devcg_online(devcg)) continue; rcu_read_unlock(); -- cgit v1.2.3-71-gd317 From 1f07d03e2069df2ecd82301936b598a3b257c6d6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 6 Aug 2013 03:32:11 -0700 Subject: net: add SNMP counters tracking incoming ECN bits With GRO/LRO processing, there is a problem because Ip[6]InReceives SNMP counters do not count the number of frames, but number of aggregated segments. Its probably too late to change this now. This patch adds four new counters, tracking number of frames, regardless of LRO/GRO, and on a per ECN status basis, for IPv4 and IPv6. Ip[6]NoECTPkts : Number of packets received with NOECT Ip[6]ECT1Pkts : Number of packets received with ECT(1) Ip[6]ECT0Pkts : Number of packets received with ECT(0) Ip[6]CEPkts : Number of packets received with Congestion Experienced lph37:~# nstat | egrep "Pkts|InReceive" IpInReceives 1634137 0.0 Ip6InReceives 3714107 0.0 Ip6InNoECTPkts 19205 0.0 Ip6InECT0Pkts 52651828 0.0 IpExtInNoECTPkts 33630 0.0 IpExtInECT0Pkts 15581379 0.0 IpExtInCEPkts 6 0.0 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/uapi/linux/snmp.h | 4 ++++ net/ipv4/ip_input.c | 8 ++++++++ net/ipv4/proc.c | 7 ++++++- net/ipv6/ip6_input.c | 6 +++++- net/ipv6/proc.c | 4 ++++ 5 files changed, 27 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index af0a674cc677..60601d28da75 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -51,6 +51,10 @@ enum IPSTATS_MIB_INBCASTOCTETS, /* InBcastOctets */ IPSTATS_MIB_OUTBCASTOCTETS, /* OutBcastOctets */ IPSTATS_MIB_CSUMERRORS, /* InCsumErrors */ + IPSTATS_MIB_NOECTPKTS, /* InNoECTPkts */ + IPSTATS_MIB_ECT1PKTS, /* InECT1Pkts */ + IPSTATS_MIB_ECT0PKTS, /* InECT0Pkts */ + IPSTATS_MIB_CEPKTS, /* InCEPkts */ __IPSTATS_MIB_MAX }; diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 15e3e683adec..054a3e97d822 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -141,6 +141,7 @@ #include #include #include +#include #include #include #include @@ -410,6 +411,13 @@ int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, if (iph->ihl < 5 || iph->version != 4) goto inhdr_error; + BUILD_BUG_ON(IPSTATS_MIB_ECT1PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_1); + BUILD_BUG_ON(IPSTATS_MIB_ECT0PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_0); + BUILD_BUG_ON(IPSTATS_MIB_CEPKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_CE); + IP_ADD_STATS_BH(dev_net(dev), + IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK), + max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs)); + if (!pskb_may_pull(skb, iph->ihl*4)) goto inhdr_error; diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 6577a1149a47..5f5fa612647f 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -111,7 +111,7 @@ static const struct snmp_mib snmp4_ipstats_list[] = { SNMP_MIB_SENTINEL }; -/* Following RFC4293 items are displayed in /proc/net/netstat */ +/* Following items are displayed in /proc/net/netstat */ static const struct snmp_mib snmp4_ipextstats_list[] = { SNMP_MIB_ITEM("InNoRoutes", IPSTATS_MIB_INNOROUTES), SNMP_MIB_ITEM("InTruncatedPkts", IPSTATS_MIB_INTRUNCATEDPKTS), @@ -125,7 +125,12 @@ static const struct snmp_mib snmp4_ipextstats_list[] = { SNMP_MIB_ITEM("OutMcastOctets", IPSTATS_MIB_OUTMCASTOCTETS), SNMP_MIB_ITEM("InBcastOctets", IPSTATS_MIB_INBCASTOCTETS), SNMP_MIB_ITEM("OutBcastOctets", IPSTATS_MIB_OUTBCASTOCTETS), + /* Non RFC4293 fields */ SNMP_MIB_ITEM("InCsumErrors", IPSTATS_MIB_CSUMERRORS), + SNMP_MIB_ITEM("InNoECTPkts", IPSTATS_MIB_NOECTPKTS), + SNMP_MIB_ITEM("InECT1Pkts", IPSTATS_MIB_ECT1PKTS), + SNMP_MIB_ITEM("InECT0Pkts", IPSTATS_MIB_ECT0PKTS), + SNMP_MIB_ITEM("InCEPkts", IPSTATS_MIB_CEPKTS), SNMP_MIB_SENTINEL }; diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 2bab2aa59745..302d6fb1ff2b 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -44,7 +44,7 @@ #include #include #include - +#include int ip6_rcv_finish(struct sk_buff *skb) @@ -109,6 +109,10 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt if (hdr->version != 6) goto err; + IP6_ADD_STATS_BH(dev_net(dev), idev, + IPSTATS_MIB_NOECTPKTS + + (ipv6_get_dsfield(hdr) & INET_ECN_MASK), + max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs)); /* * RFC4291 2.5.3 * A packet received on an interface with a destination address diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 51c3285b5d9b..091d066a57b3 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -91,6 +91,10 @@ static const struct snmp_mib snmp6_ipstats_list[] = { SNMP_MIB_ITEM("Ip6InBcastOctets", IPSTATS_MIB_INBCASTOCTETS), SNMP_MIB_ITEM("Ip6OutBcastOctets", IPSTATS_MIB_OUTBCASTOCTETS), /* IPSTATS_MIB_CSUMERRORS is not relevant in IPv6 (no checksum) */ + SNMP_MIB_ITEM("Ip6InNoECTPkts", IPSTATS_MIB_NOECTPKTS), + SNMP_MIB_ITEM("Ip6InECT1Pkts", IPSTATS_MIB_ECT1PKTS), + SNMP_MIB_ITEM("Ip6InECT0Pkts", IPSTATS_MIB_ECT0PKTS), + SNMP_MIB_ITEM("Ip6InCEPkts", IPSTATS_MIB_CEPKTS), SNMP_MIB_SENTINEL }; -- cgit v1.2.3-71-gd317 From e99a03c6f50a699d63b5751e3e88f84e43b037a7 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 1 Aug 2013 15:01:08 -0400 Subject: jbd: use a single printk for jbd_debug() Backport of jbd2 commit 169f1a2a87aae44034da4b9f81be1683d33de6d0 ("jbd2: use a single printk for jbd_debug()") Since the jbd_debug() is implemented with two separate printk() calls, it can lead to corrupted and misleading debug output like the following (see lines marked with "*"): [ 290.339362] (fs/jbd2/journal.c, 203): kjournald2: kjournald2 wakes [ 290.339365] (fs/jbd2/journal.c, 155): kjournald2: commit_sequence=42103, commit_request=42104 [ 290.339369] (fs/jbd2/journal.c, 158): kjournald2: OK, requests differ [* 290.339376] (fs/jbd2/journal.c, 648): jbd2_log_wait_commit: [* 290.339379] (fs/jbd2/commit.c, 370): jbd2_journal_commit_transaction: JBD2: want 42104, j_commit_sequence=42103 [* 290.339382] JBD2: starting commit of transaction 42104 [ 290.339410] (fs/jbd2/revoke.c, 566): jbd2_journal_write_revoke_records: Wrote 0 revoke records [ 290.376555] (fs/jbd2/commit.c, 1088): jbd2_journal_commit_transaction: JBD2: commit 42104 complete, head 42079 i.e. the debug output from log_wait_commit and journal_commit_transaction have become interleaved. The output should have been: (fs/jbd2/journal.c, 648): jbd2_log_wait_commit: JBD2: want 42104, j_commit_sequence=42103 (fs/jbd2/commit.c, 370): jbd2_journal_commit_transaction: JBD2: starting commit of transaction 42104 It is expected that this is not easy to replicate -- I was only able to cause it on preempt-rt kernels, and even then only under heavy I/O load. Signed-off-by: Paul Gortmaker Signed-off-by: Jan Kara --- fs/jbd/journal.c | 18 ++++++++++++++++++ include/linux/jbd.h | 15 ++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c index 6510d6355729..2d04f9afafd7 100644 --- a/fs/jbd/journal.c +++ b/fs/jbd/journal.c @@ -90,6 +90,24 @@ static int journal_convert_superblock_v1(journal_t *, journal_superblock_t *); static void __journal_abort_soft (journal_t *journal, int errno); static const char *journal_dev_name(journal_t *journal, char *buffer); +#ifdef CONFIG_JBD_DEBUG +void __jbd_debug(int level, const char *file, const char *func, + unsigned int line, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (level > journal_enable_debug) + return; + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + printk(KERN_DEBUG "%s: (%s, %u): %pV\n", file, func, line, &vaf); + va_end(args); +} +EXPORT_SYMBOL(__jbd_debug); +#endif + /* * Helper function used to manage commit timeouts */ diff --git a/include/linux/jbd.h b/include/linux/jbd.h index bc3e57d367fc..31229e0be90b 100644 --- a/include/linux/jbd.h +++ b/include/linux/jbd.h @@ -57,16 +57,13 @@ #define JBD_EXPENSIVE_CHECKING extern u8 journal_enable_debug; -#define jbd_debug(n, f, a...) \ - do { \ - if ((n) <= journal_enable_debug) { \ - printk (KERN_DEBUG "(%s, %d): %s: ", \ - __FILE__, __LINE__, __func__); \ - printk (f, ## a); \ - } \ - } while (0) +void __jbd_debug(int level, const char *file, const char *func, + unsigned int line, const char *fmt, ...); + +#define jbd_debug(n, fmt, a...) \ + __jbd_debug((n), __FILE__, __func__, __LINE__, (fmt), ##a) #else -#define jbd_debug(f, a...) /**/ +#define jbd_debug(n, fmt, a...) /**/ #endif static inline void *jbd_alloc(size_t size, gfp_t flags) -- cgit v1.2.3-71-gd317 From c655bc6896b94ee0223393f26155c6daf1e2d148 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 29 Jul 2013 15:41:55 +0200 Subject: netfilter: nf_conntrack: don't send destroy events from iterator Let nf_ct_delete handle delivery of the DESTROY event. Based on earlier patch from Pablo Neira. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack.h | 4 +++- net/ipv4/netfilter/ipt_MASQUERADE.c | 2 +- net/ipv6/netfilter/ip6t_MASQUERADE.c | 2 +- net/netfilter/nf_conntrack_core.c | 36 ++++-------------------------------- net/netfilter/nf_conntrack_proto.c | 4 ++-- net/netfilter/nf_nat_core.c | 6 +++--- 6 files changed, 14 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index e5eb8b62538c..0c1288a50e8b 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -248,7 +248,9 @@ extern void nf_ct_untracked_status_or(unsigned long bits); /* Iterate over all conntracks: if iter returns true, it's deleted. */ extern void -nf_ct_iterate_cleanup(struct net *net, int (*iter)(struct nf_conn *i, void *data), void *data); +nf_ct_iterate_cleanup(struct net *net, + int (*iter)(struct nf_conn *i, void *data), + void *data, u32 portid, int report); extern void nf_conntrack_free(struct nf_conn *ct); extern struct nf_conn * nf_conntrack_alloc(struct net *net, u16 zone, diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index 30e4de940567..00352ce0f0de 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -118,7 +118,7 @@ static int masq_device_event(struct notifier_block *this, NF_CT_ASSERT(dev->ifindex != 0); nf_ct_iterate_cleanup(net, device_cmp, - (void *)(long)dev->ifindex); + (void *)(long)dev->ifindex, 0, 0); } return NOTIFY_DONE; diff --git a/net/ipv6/netfilter/ip6t_MASQUERADE.c b/net/ipv6/netfilter/ip6t_MASQUERADE.c index 47bff6107519..3e4e92d5e157 100644 --- a/net/ipv6/netfilter/ip6t_MASQUERADE.c +++ b/net/ipv6/netfilter/ip6t_MASQUERADE.c @@ -76,7 +76,7 @@ static int masq_device_event(struct notifier_block *this, if (event == NETDEV_DOWN) nf_ct_iterate_cleanup(net, device_cmp, - (void *)(long)dev->ifindex); + (void *)(long)dev->ifindex, 0, 0); return NOTIFY_DONE; } diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 0934611ff9f3..da6f1787a102 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1246,7 +1246,7 @@ found: void nf_ct_iterate_cleanup(struct net *net, int (*iter)(struct nf_conn *i, void *data), - void *data) + void *data, u32 portid, int report) { struct nf_conn *ct; unsigned int bucket = 0; @@ -1254,7 +1254,7 @@ void nf_ct_iterate_cleanup(struct net *net, while ((ct = get_next_corpse(net, iter, data, &bucket)) != NULL) { /* Time to push up daises... */ if (del_timer(&ct->timeout)) - death_by_timeout((unsigned long)ct); + nf_ct_delete(ct, portid, report); /* ... else the timer will get him soon. */ @@ -1263,30 +1263,6 @@ void nf_ct_iterate_cleanup(struct net *net, } EXPORT_SYMBOL_GPL(nf_ct_iterate_cleanup); -struct __nf_ct_flush_report { - u32 portid; - int report; -}; - -static int kill_report(struct nf_conn *i, void *data) -{ - struct __nf_ct_flush_report *fr = (struct __nf_ct_flush_report *)data; - struct nf_conn_tstamp *tstamp; - - tstamp = nf_conn_tstamp_find(i); - if (tstamp && tstamp->stop == 0) - tstamp->stop = ktime_to_ns(ktime_get_real()); - - /* If we fail to deliver the event, death_by_timeout() will retry */ - if (nf_conntrack_event_report(IPCT_DESTROY, i, - fr->portid, fr->report) < 0) - return 1; - - /* Avoid the delivery of the destroy event in death_by_timeout(). */ - set_bit(IPS_DYING_BIT, &i->status); - return 1; -} - static int kill_all(struct nf_conn *i, void *data) { return 1; @@ -1304,11 +1280,7 @@ EXPORT_SYMBOL_GPL(nf_ct_free_hashtable); void nf_conntrack_flush_report(struct net *net, u32 portid, int report) { - struct __nf_ct_flush_report fr = { - .portid = portid, - .report = report, - }; - nf_ct_iterate_cleanup(net, kill_report, &fr); + nf_ct_iterate_cleanup(net, kill_all, NULL, portid, report); } EXPORT_SYMBOL_GPL(nf_conntrack_flush_report); @@ -1389,7 +1361,7 @@ void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list) i_see_dead_people: busy = 0; list_for_each_entry(net, net_exit_list, exit_list) { - nf_ct_iterate_cleanup(net, kill_all, NULL); + nf_ct_iterate_cleanup(net, kill_all, NULL, 0, 0); nf_ct_release_dying_list(net); if (atomic_read(&net->ct.count) != 0) busy = 1; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 0ab9636ac57e..ce3004156eeb 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -281,7 +281,7 @@ void nf_ct_l3proto_pernet_unregister(struct net *net, nf_ct_l3proto_unregister_sysctl(net, proto); /* Remove all contrack entries for this protocol */ - nf_ct_iterate_cleanup(net, kill_l3proto, proto); + nf_ct_iterate_cleanup(net, kill_l3proto, proto, 0, 0); } EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_unregister); @@ -476,7 +476,7 @@ void nf_ct_l4proto_pernet_unregister(struct net *net, nf_ct_l4proto_unregister_sysctl(net, pn, l4proto); /* Remove all contrack entries for this protocol */ - nf_ct_iterate_cleanup(net, kill_l4proto, l4proto); + nf_ct_iterate_cleanup(net, kill_l4proto, l4proto, 0, 0); } EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_unregister); diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 038eee5c8f85..6ff808375b5e 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -497,7 +497,7 @@ static void nf_nat_l4proto_clean(u8 l3proto, u8 l4proto) rtnl_lock(); for_each_net(net) - nf_ct_iterate_cleanup(net, nf_nat_proto_remove, &clean); + nf_ct_iterate_cleanup(net, nf_nat_proto_remove, &clean, 0, 0); rtnl_unlock(); } @@ -511,7 +511,7 @@ static void nf_nat_l3proto_clean(u8 l3proto) rtnl_lock(); for_each_net(net) - nf_ct_iterate_cleanup(net, nf_nat_proto_remove, &clean); + nf_ct_iterate_cleanup(net, nf_nat_proto_remove, &clean, 0, 0); rtnl_unlock(); } @@ -749,7 +749,7 @@ static void __net_exit nf_nat_net_exit(struct net *net) { struct nf_nat_proto_clean clean = {}; - nf_ct_iterate_cleanup(net, &nf_nat_proto_remove, &clean); + nf_ct_iterate_cleanup(net, &nf_nat_proto_remove, &clean, 0, 0); synchronize_rcu(); nf_ct_free_hashtable(net->ct.nat_bysource, net->ct.nat_htable_size); } -- cgit v1.2.3-71-gd317 From 26e7164537380479bf96e852df6ab42ebe50e836 Mon Sep 17 00:00:00 2001 From: Darren Etheridge Date: Mon, 5 Aug 2013 17:02:30 -0500 Subject: video: da8xx-fb: improve readability of code Change the lcd_disable_raster funtion from using a bool to an enum as the function is very confusing with the current api. This helps make it clearer what the parameter is really doing. Signed-off-by: Darren Etheridge Signed-off-by: Tomi Valkeinen --- drivers/video/da8xx-fb.c | 24 +++++++++++++----------- include/video/da8xx-fb.h | 5 +++++ 2 files changed, 18 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index 6beb88d7b7d7..93753828eb08 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -271,7 +271,8 @@ static inline void lcd_enable_raster(void) } /* Disable the Raster Engine of the LCD Controller */ -static inline void lcd_disable_raster(bool wait_for_frame_done) +static inline void lcd_disable_raster(enum da8xx_frame_complete + wait_for_frame_done) { u32 reg; int ret; @@ -283,7 +284,8 @@ static inline void lcd_disable_raster(bool wait_for_frame_done) /* return if already disabled */ return; - if ((wait_for_frame_done == true) && (lcd_revision == LCD_VERSION_2)) { + if ((wait_for_frame_done == DA8XX_FRAME_WAIT) && + (lcd_revision == LCD_VERSION_2)) { frame_done_flag = 0; ret = wait_event_interruptible_timeout(frame_done_wq, frame_done_flag != 0, @@ -771,7 +773,7 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg) u32 stat = lcdc_read(LCD_MASKED_STAT_REG); if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { - lcd_disable_raster(false); + lcd_disable_raster(DA8XX_FRAME_NOWAIT); lcdc_write(stat, LCD_MASKED_STAT_REG); lcd_enable_raster(); } else if (stat & LCD_PL_LOAD_DONE) { @@ -781,7 +783,7 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg) * interrupt via the following write to the status register. If * this is done after then one gets multiple PL done interrupts. */ - lcd_disable_raster(false); + lcd_disable_raster(DA8XX_FRAME_NOWAIT); lcdc_write(stat, LCD_MASKED_STAT_REG); @@ -834,7 +836,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg) u32 reg_ras; if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { - lcd_disable_raster(false); + lcd_disable_raster(DA8XX_FRAME_NOWAIT); lcdc_write(stat, LCD_STAT_REG); lcd_enable_raster(); } else if (stat & LCD_PL_LOAD_DONE) { @@ -844,7 +846,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg) * interrupt via the following write to the status register. If * this is done after then one gets multiple PL done interrupts. */ - lcd_disable_raster(false); + lcd_disable_raster(DA8XX_FRAME_NOWAIT); lcdc_write(stat, LCD_STAT_REG); @@ -986,7 +988,7 @@ static int lcd_da8xx_cpufreq_transition(struct notifier_block *nb, if (val == CPUFREQ_POSTCHANGE) { if (par->lcd_fck_rate != clk_get_rate(par->lcdc_clk)) { par->lcd_fck_rate = clk_get_rate(par->lcdc_clk); - lcd_disable_raster(true); + lcd_disable_raster(DA8XX_FRAME_WAIT); da8xx_fb_calc_config_clk_divider(par, &par->mode); if (par->blank == FB_BLANK_UNBLANK) lcd_enable_raster(); @@ -1024,7 +1026,7 @@ static int fb_remove(struct platform_device *dev) if (par->panel_power_ctrl) par->panel_power_ctrl(0); - lcd_disable_raster(true); + lcd_disable_raster(DA8XX_FRAME_WAIT); lcdc_write(0, LCD_RASTER_CTRL_REG); /* disable DMA */ @@ -1140,7 +1142,7 @@ static int cfb_blank(int blank, struct fb_info *info) if (par->panel_power_ctrl) par->panel_power_ctrl(0); - lcd_disable_raster(true); + lcd_disable_raster(DA8XX_FRAME_WAIT); break; default: ret = -EINVAL; @@ -1208,7 +1210,7 @@ static int da8xxfb_set_par(struct fb_info *info) bool raster = da8xx_fb_is_raster_enabled(); if (raster) - lcd_disable_raster(true); + lcd_disable_raster(DA8XX_FRAME_WAIT); fb_var_to_videomode(&par->mode, &info->var); @@ -1569,7 +1571,7 @@ static int fb_suspend(struct platform_device *dev, pm_message_t state) par->panel_power_ctrl(0); fb_set_suspend(info, 1); - lcd_disable_raster(true); + lcd_disable_raster(DA8XX_FRAME_WAIT); lcd_context_save(); pm_runtime_put_sync(&dev->dev); console_unlock(); diff --git a/include/video/da8xx-fb.h b/include/video/da8xx-fb.h index f88825928dd1..efed3c3383d6 100644 --- a/include/video/da8xx-fb.h +++ b/include/video/da8xx-fb.h @@ -23,6 +23,11 @@ enum raster_load_mode { LOAD_PALETTE, }; +enum da8xx_frame_complete { + DA8XX_FRAME_WAIT, + DA8XX_FRAME_NOWAIT, +}; + struct da8xx_lcdc_platform_data { const char manu_name[10]; void *controller_data; -- cgit v1.2.3-71-gd317 From 3fa4d7344be0afebd80382ffeea6b1787cccf971 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 26 Jul 2013 12:16:42 +0200 Subject: usb: phy: rename nop_usb_xceiv => usb_phy_gen_xceiv The "nop" driver isn't a do-nothing-stub but supports a couple functions like clock on/off or is able to use a voltage regulator. This patch simply renames the driver to "generic" since it is easy possible to extend it by a simple function istead of writing a complete driver. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi --- arch/arm/mach-omap2/board-omap3beagle.c | 4 +- arch/arm/mach-omap2/board-omap3evm.c | 4 +- arch/arm/mach-omap2/board-omap3pandora.c | 2 +- arch/arm/mach-omap2/usb-host.c | 10 +- drivers/usb/dwc3/dwc3-exynos.c | 8 +- drivers/usb/dwc3/dwc3-pci.c | 8 +- drivers/usb/musb/am35x.c | 2 +- drivers/usb/musb/blackfin.c | 2 +- drivers/usb/musb/da8xx.c | 2 +- drivers/usb/musb/davinci.c | 2 +- drivers/usb/musb/musb_dsps.c | 2 +- drivers/usb/musb/tusb6010.c | 2 +- drivers/usb/phy/Makefile | 2 +- drivers/usb/phy/phy-generic.c | 292 +++++++++++++++++++++++++++++++ drivers/usb/phy/phy-nop.c | 292 ------------------------------- include/linux/usb/nop-usb-xceiv.h | 29 --- include/linux/usb/usb_phy_gen_xceiv.h | 29 +++ 17 files changed, 346 insertions(+), 346 deletions(-) create mode 100644 drivers/usb/phy/phy-generic.c delete mode 100644 drivers/usb/phy/phy-nop.c delete mode 100644 include/linux/usb/nop-usb-xceiv.h create mode 100644 include/linux/usb/usb_phy_gen_xceiv.h (limited to 'include') diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c index 04c116555412..1c6ae5f5bae7 100644 --- a/arch/arm/mach-omap2/board-omap3beagle.c +++ b/arch/arm/mach-omap2/board-omap3beagle.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -279,7 +279,7 @@ static struct regulator_consumer_supply beagle_vsim_supply[] = { static struct gpio_led gpio_leds[]; /* PHY's VCC regulator might be added later, so flag that we need it */ -static struct nop_usb_xceiv_platform_data hsusb2_phy_data = { +static struct usb_phy_gen_xceiv_platform_data hsusb2_phy_data = { .needs_vcc = true, }; diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c index 8c026269baca..52bdddd41e0e 100644 --- a/arch/arm/mach-omap2/board-omap3evm.c +++ b/arch/arm/mach-omap2/board-omap3evm.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include @@ -468,7 +468,7 @@ struct wl12xx_platform_data omap3evm_wlan_data __initdata = { static struct regulator_consumer_supply omap3evm_vaux2_supplies[] = { REGULATOR_SUPPLY("VDD_CSIPHY1", "omap3isp"), /* OMAP ISP */ REGULATOR_SUPPLY("VDD_CSIPHY2", "omap3isp"), /* OMAP ISP */ - REGULATOR_SUPPLY("vcc", "nop_usb_xceiv.2"), /* hsusb port 2 */ + REGULATOR_SUPPLY("vcc", "usb_phy_gen_xceiv.2"), /* hsusb port 2 */ REGULATOR_SUPPLY("vaux2", NULL), }; diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index b1547a0edfcd..d2b455e70486 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -352,7 +352,7 @@ static struct regulator_consumer_supply pandora_vcc_lcd_supply[] = { }; static struct regulator_consumer_supply pandora_usb_phy_supply[] = { - REGULATOR_SUPPLY("vcc", "nop_usb_xceiv.2"), /* hsusb port 2 */ + REGULATOR_SUPPLY("vcc", "usb_phy_gen_xceiv.2"), /* hsusb port 2 */ }; /* ads7846 on SPI and 2 nub controllers on I2C */ diff --git a/arch/arm/mach-omap2/usb-host.c b/arch/arm/mach-omap2/usb-host.c index 2eb19d4d0aa1..e83a6a4b184a 100644 --- a/arch/arm/mach-omap2/usb-host.c +++ b/arch/arm/mach-omap2/usb-host.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include "soc.h" #include "omap_device.h" @@ -349,7 +349,7 @@ static struct fixed_voltage_config hsusb_reg_config = { /* .init_data filled later */ }; -static const char *nop_name = "nop_usb_xceiv"; /* NOP PHY driver */ +static const char *nop_name = "usb_phy_gen_xceiv"; /* NOP PHY driver */ static const char *reg_name = "reg-fixed-voltage"; /* Regulator driver */ /** @@ -460,9 +460,9 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys) pdevinfo.name = nop_name; pdevinfo.id = phy->port; pdevinfo.data = phy->platform_data; - pdevinfo.size_data = sizeof(struct nop_usb_xceiv_platform_data); - - scnprintf(phy_id, MAX_STR, "nop_usb_xceiv.%d", + pdevinfo.size_data = + sizeof(struct usb_phy_gen_xceiv_platform_data); + scnprintf(phy_id, MAX_STR, "usb_phy_gen_xceiv.%d", phy->port); pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(pdev)) { diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 8ce9d7fd6cfc..a179c5a54b39 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include @@ -34,13 +34,13 @@ struct dwc3_exynos { static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) { - struct nop_usb_xceiv_platform_data pdata; + struct usb_phy_gen_xceiv_platform_data pdata; struct platform_device *pdev; int ret; memset(&pdata, 0x00, sizeof(pdata)); - pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO); + pdev = platform_device_alloc("usb_phy_gen_xceiv", PLATFORM_DEVID_AUTO); if (!pdev) return -ENOMEM; @@ -51,7 +51,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos) if (ret) goto err1; - pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO); + pdev = platform_device_alloc("usb_phy_gen_xceiv", PLATFORM_DEVID_AUTO); if (!pdev) { ret = -ENOMEM; goto err1; diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index ed07ec04a962..87ea3d503fc5 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -43,7 +43,7 @@ #include #include -#include +#include /* FIXME define these in */ #define PCI_VENDOR_ID_SYNOPSYS 0x16c3 @@ -58,13 +58,13 @@ struct dwc3_pci { static int dwc3_pci_register_phys(struct dwc3_pci *glue) { - struct nop_usb_xceiv_platform_data pdata; + struct usb_phy_gen_xceiv_platform_data pdata; struct platform_device *pdev; int ret; memset(&pdata, 0x00, sizeof(pdata)); - pdev = platform_device_alloc("nop_usb_xceiv", 0); + pdev = platform_device_alloc("usb_phy_gen_xceiv", 0); if (!pdev) return -ENOMEM; @@ -75,7 +75,7 @@ static int dwc3_pci_register_phys(struct dwc3_pci *glue) if (ret) goto err1; - pdev = platform_device_alloc("nop_usb_xceiv", 1); + pdev = platform_device_alloc("usb_phy_gen_xceiv", 1); if (!pdev) { ret = -ENOMEM; goto err1; diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 2231850c0625..5733a209ab95 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include "musb_core.h" diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 6ba8439bd5a6..195e96674fff 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 0da6f648a9fe..ccc6b63bc380 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index f8aeaf2e2cd1..91f300e93cf4 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 5233804d66b1..55fe3c292cd2 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 2c06a8969a9f..20f182c6184a 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include "musb_core.h" diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 070eca3af18b..24c5816409fd 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -14,7 +14,7 @@ phy-fsl-usb2-objs := phy-fsl-usb.o phy-fsm-usb.o obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb2.o obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o -obj-$(CONFIG_NOP_USB_XCEIV) += phy-nop.o +obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c new file mode 100644 index 000000000000..f379b7ded037 --- /dev/null +++ b/drivers/usb/phy/phy-generic.c @@ -0,0 +1,292 @@ +/* + * drivers/usb/otg/nop-usb-xceiv.c + * + * NOP USB transceiver for all USB transceiver which are either built-in + * into USB IP or which are mostly autonomous. + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Ajay Kumar Gupta + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Current status: + * This provides a "nop" transceiver for PHYs which are + * autonomous such as isp1504, isp1707, etc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct usb_phy_gen_xceiv { + struct usb_phy phy; + struct device *dev; + struct clk *clk; + struct regulator *vcc; + struct regulator *reset; +}; + +static struct platform_device *pd; + +void usb_nop_xceiv_register(void) +{ + if (pd) + return; + pd = platform_device_register_simple("usb_phy_gen_xceiv", -1, NULL, 0); + if (!pd) { + pr_err("Unable to register generic usb transceiver\n"); + return; + } +} +EXPORT_SYMBOL(usb_nop_xceiv_register); + +void usb_nop_xceiv_unregister(void) +{ + platform_device_unregister(pd); + pd = NULL; +} +EXPORT_SYMBOL(usb_nop_xceiv_unregister); + +static int nop_set_suspend(struct usb_phy *x, int suspend) +{ + return 0; +} + +static int nop_init(struct usb_phy *phy) +{ + struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); + + if (!IS_ERR(nop->vcc)) { + if (regulator_enable(nop->vcc)) + dev_err(phy->dev, "Failed to enable power\n"); + } + + if (!IS_ERR(nop->clk)) + clk_enable(nop->clk); + + if (!IS_ERR(nop->reset)) { + /* De-assert RESET */ + if (regulator_enable(nop->reset)) + dev_err(phy->dev, "Failed to de-assert reset\n"); + } + + return 0; +} + +static void nop_shutdown(struct usb_phy *phy) +{ + struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); + + if (!IS_ERR(nop->reset)) { + /* Assert RESET */ + if (regulator_disable(nop->reset)) + dev_err(phy->dev, "Failed to assert reset\n"); + } + + if (!IS_ERR(nop->clk)) + clk_disable(nop->clk); + + if (!IS_ERR(nop->vcc)) { + if (regulator_disable(nop->vcc)) + dev_err(phy->dev, "Failed to disable power\n"); + } +} + +static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) +{ + if (!otg) + return -ENODEV; + + if (!gadget) { + otg->gadget = NULL; + return -ENODEV; + } + + otg->gadget = gadget; + otg->phy->state = OTG_STATE_B_IDLE; + return 0; +} + +static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + + if (!host) { + otg->host = NULL; + return -ENODEV; + } + + otg->host = host; + return 0; +} + +static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_phy_gen_xceiv_platform_data *pdata = pdev->dev.platform_data; + struct usb_phy_gen_xceiv *nop; + enum usb_phy_type type = USB_PHY_TYPE_USB2; + int err; + u32 clk_rate = 0; + bool needs_vcc = false; + bool needs_reset = false; + + nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); + if (!nop) + return -ENOMEM; + + nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg), + GFP_KERNEL); + if (!nop->phy.otg) + return -ENOMEM; + + if (dev->of_node) { + struct device_node *node = dev->of_node; + + if (of_property_read_u32(node, "clock-frequency", &clk_rate)) + clk_rate = 0; + + needs_vcc = of_property_read_bool(node, "vcc-supply"); + needs_reset = of_property_read_bool(node, "reset-supply"); + + } else if (pdata) { + type = pdata->type; + clk_rate = pdata->clk_rate; + needs_vcc = pdata->needs_vcc; + needs_reset = pdata->needs_reset; + } + + nop->clk = devm_clk_get(&pdev->dev, "main_clk"); + if (IS_ERR(nop->clk)) { + dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n", + PTR_ERR(nop->clk)); + } + + if (!IS_ERR(nop->clk) && clk_rate) { + err = clk_set_rate(nop->clk, clk_rate); + if (err) { + dev_err(&pdev->dev, "Error setting clock rate\n"); + return err; + } + } + + if (!IS_ERR(nop->clk)) { + err = clk_prepare(nop->clk); + if (err) { + dev_err(&pdev->dev, "Error preparing clock\n"); + return err; + } + } + + nop->vcc = devm_regulator_get(&pdev->dev, "vcc"); + if (IS_ERR(nop->vcc)) { + dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", + PTR_ERR(nop->vcc)); + if (needs_vcc) + return -EPROBE_DEFER; + } + + nop->reset = devm_regulator_get(&pdev->dev, "reset"); + if (IS_ERR(nop->reset)) { + dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", + PTR_ERR(nop->reset)); + if (needs_reset) + return -EPROBE_DEFER; + } + + nop->dev = &pdev->dev; + nop->phy.dev = nop->dev; + nop->phy.label = "nop-xceiv"; + nop->phy.set_suspend = nop_set_suspend; + nop->phy.init = nop_init; + nop->phy.shutdown = nop_shutdown; + nop->phy.state = OTG_STATE_UNDEFINED; + nop->phy.type = type; + + nop->phy.otg->phy = &nop->phy; + nop->phy.otg->set_host = nop_set_host; + nop->phy.otg->set_peripheral = nop_set_peripheral; + + err = usb_add_phy_dev(&nop->phy); + if (err) { + dev_err(&pdev->dev, "can't register transceiver, err: %d\n", + err); + goto err_add; + } + + platform_set_drvdata(pdev, nop); + + ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); + + return 0; + +err_add: + if (!IS_ERR(nop->clk)) + clk_unprepare(nop->clk); + return err; +} + +static int usb_phy_gen_xceiv_remove(struct platform_device *pdev) +{ + struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev); + + if (!IS_ERR(nop->clk)) + clk_unprepare(nop->clk); + + usb_remove_phy(&nop->phy); + + return 0; +} + +static const struct of_device_id nop_xceiv_dt_ids[] = { + { .compatible = "usb-nop-xceiv" }, + { } +}; + +MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); + +static struct platform_driver usb_phy_gen_xceiv_driver = { + .probe = usb_phy_gen_xceiv_probe, + .remove = usb_phy_gen_xceiv_remove, + .driver = { + .name = "usb_phy_gen_xceiv", + .owner = THIS_MODULE, + .of_match_table = nop_xceiv_dt_ids, + }, +}; + +static int __init usb_phy_gen_xceiv_init(void) +{ + return platform_driver_register(&usb_phy_gen_xceiv_driver); +} +subsys_initcall(usb_phy_gen_xceiv_init); + +static void __exit usb_phy_gen_xceiv_exit(void) +{ + platform_driver_unregister(&usb_phy_gen_xceiv_driver); +} +module_exit(usb_phy_gen_xceiv_exit); + +MODULE_ALIAS("platform:usb_phy_gen_xceiv"); +MODULE_AUTHOR("Texas Instruments Inc"); +MODULE_DESCRIPTION("NOP USB Transceiver driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/phy/phy-nop.c b/drivers/usb/phy/phy-nop.c deleted file mode 100644 index 55445e5d72e5..000000000000 --- a/drivers/usb/phy/phy-nop.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * drivers/usb/otg/nop-usb-xceiv.c - * - * NOP USB transceiver for all USB transceiver which are either built-in - * into USB IP or which are mostly autonomous. - * - * Copyright (C) 2009 Texas Instruments Inc - * Author: Ajay Kumar Gupta - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Current status: - * This provides a "nop" transceiver for PHYs which are - * autonomous such as isp1504, isp1707, etc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct nop_usb_xceiv { - struct usb_phy phy; - struct device *dev; - struct clk *clk; - struct regulator *vcc; - struct regulator *reset; -}; - -static struct platform_device *pd; - -void usb_nop_xceiv_register(void) -{ - if (pd) - return; - pd = platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0); - if (!pd) { - printk(KERN_ERR "Unable to register usb nop transceiver\n"); - return; - } -} -EXPORT_SYMBOL(usb_nop_xceiv_register); - -void usb_nop_xceiv_unregister(void) -{ - platform_device_unregister(pd); - pd = NULL; -} -EXPORT_SYMBOL(usb_nop_xceiv_unregister); - -static int nop_set_suspend(struct usb_phy *x, int suspend) -{ - return 0; -} - -static int nop_init(struct usb_phy *phy) -{ - struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); - - if (!IS_ERR(nop->vcc)) { - if (regulator_enable(nop->vcc)) - dev_err(phy->dev, "Failed to enable power\n"); - } - - if (!IS_ERR(nop->clk)) - clk_enable(nop->clk); - - if (!IS_ERR(nop->reset)) { - /* De-assert RESET */ - if (regulator_enable(nop->reset)) - dev_err(phy->dev, "Failed to de-assert reset\n"); - } - - return 0; -} - -static void nop_shutdown(struct usb_phy *phy) -{ - struct nop_usb_xceiv *nop = dev_get_drvdata(phy->dev); - - if (!IS_ERR(nop->reset)) { - /* Assert RESET */ - if (regulator_disable(nop->reset)) - dev_err(phy->dev, "Failed to assert reset\n"); - } - - if (!IS_ERR(nop->clk)) - clk_disable(nop->clk); - - if (!IS_ERR(nop->vcc)) { - if (regulator_disable(nop->vcc)) - dev_err(phy->dev, "Failed to disable power\n"); - } -} - -static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) -{ - if (!otg) - return -ENODEV; - - if (!gadget) { - otg->gadget = NULL; - return -ENODEV; - } - - otg->gadget = gadget; - otg->phy->state = OTG_STATE_B_IDLE; - return 0; -} - -static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - if (!otg) - return -ENODEV; - - if (!host) { - otg->host = NULL; - return -ENODEV; - } - - otg->host = host; - return 0; -} - -static int nop_usb_xceiv_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data; - struct nop_usb_xceiv *nop; - enum usb_phy_type type = USB_PHY_TYPE_USB2; - int err; - u32 clk_rate = 0; - bool needs_vcc = false; - bool needs_reset = false; - - nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); - if (!nop) - return -ENOMEM; - - nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg), - GFP_KERNEL); - if (!nop->phy.otg) - return -ENOMEM; - - if (dev->of_node) { - struct device_node *node = dev->of_node; - - if (of_property_read_u32(node, "clock-frequency", &clk_rate)) - clk_rate = 0; - - needs_vcc = of_property_read_bool(node, "vcc-supply"); - needs_reset = of_property_read_bool(node, "reset-supply"); - - } else if (pdata) { - type = pdata->type; - clk_rate = pdata->clk_rate; - needs_vcc = pdata->needs_vcc; - needs_reset = pdata->needs_reset; - } - - nop->clk = devm_clk_get(&pdev->dev, "main_clk"); - if (IS_ERR(nop->clk)) { - dev_dbg(&pdev->dev, "Can't get phy clock: %ld\n", - PTR_ERR(nop->clk)); - } - - if (!IS_ERR(nop->clk) && clk_rate) { - err = clk_set_rate(nop->clk, clk_rate); - if (err) { - dev_err(&pdev->dev, "Error setting clock rate\n"); - return err; - } - } - - if (!IS_ERR(nop->clk)) { - err = clk_prepare(nop->clk); - if (err) { - dev_err(&pdev->dev, "Error preparing clock\n"); - return err; - } - } - - nop->vcc = devm_regulator_get(&pdev->dev, "vcc"); - if (IS_ERR(nop->vcc)) { - dev_dbg(&pdev->dev, "Error getting vcc regulator: %ld\n", - PTR_ERR(nop->vcc)); - if (needs_vcc) - return -EPROBE_DEFER; - } - - nop->reset = devm_regulator_get(&pdev->dev, "reset"); - if (IS_ERR(nop->reset)) { - dev_dbg(&pdev->dev, "Error getting reset regulator: %ld\n", - PTR_ERR(nop->reset)); - if (needs_reset) - return -EPROBE_DEFER; - } - - nop->dev = &pdev->dev; - nop->phy.dev = nop->dev; - nop->phy.label = "nop-xceiv"; - nop->phy.set_suspend = nop_set_suspend; - nop->phy.init = nop_init; - nop->phy.shutdown = nop_shutdown; - nop->phy.state = OTG_STATE_UNDEFINED; - nop->phy.type = type; - - nop->phy.otg->phy = &nop->phy; - nop->phy.otg->set_host = nop_set_host; - nop->phy.otg->set_peripheral = nop_set_peripheral; - - err = usb_add_phy_dev(&nop->phy); - if (err) { - dev_err(&pdev->dev, "can't register transceiver, err: %d\n", - err); - goto err_add; - } - - platform_set_drvdata(pdev, nop); - - ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier); - - return 0; - -err_add: - if (!IS_ERR(nop->clk)) - clk_unprepare(nop->clk); - return err; -} - -static int nop_usb_xceiv_remove(struct platform_device *pdev) -{ - struct nop_usb_xceiv *nop = platform_get_drvdata(pdev); - - if (!IS_ERR(nop->clk)) - clk_unprepare(nop->clk); - - usb_remove_phy(&nop->phy); - - return 0; -} - -static const struct of_device_id nop_xceiv_dt_ids[] = { - { .compatible = "usb-nop-xceiv" }, - { } -}; - -MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); - -static struct platform_driver nop_usb_xceiv_driver = { - .probe = nop_usb_xceiv_probe, - .remove = nop_usb_xceiv_remove, - .driver = { - .name = "nop_usb_xceiv", - .owner = THIS_MODULE, - .of_match_table = nop_xceiv_dt_ids, - }, -}; - -static int __init nop_usb_xceiv_init(void) -{ - return platform_driver_register(&nop_usb_xceiv_driver); -} -subsys_initcall(nop_usb_xceiv_init); - -static void __exit nop_usb_xceiv_exit(void) -{ - platform_driver_unregister(&nop_usb_xceiv_driver); -} -module_exit(nop_usb_xceiv_exit); - -MODULE_ALIAS("platform:nop_usb_xceiv"); -MODULE_AUTHOR("Texas Instruments Inc"); -MODULE_DESCRIPTION("NOP USB Transceiver driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/usb/nop-usb-xceiv.h b/include/linux/usb/nop-usb-xceiv.h deleted file mode 100644 index 148d35171aac..000000000000 --- a/include/linux/usb/nop-usb-xceiv.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __LINUX_USB_NOP_XCEIV_H -#define __LINUX_USB_NOP_XCEIV_H - -#include - -struct nop_usb_xceiv_platform_data { - enum usb_phy_type type; - unsigned long clk_rate; - - /* if set fails with -EPROBE_DEFER if can't get regulator */ - unsigned int needs_vcc:1; - unsigned int needs_reset:1; -}; - -#if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE)) -/* sometimes transceivers are accessed only through e.g. ULPI */ -extern void usb_nop_xceiv_register(void); -extern void usb_nop_xceiv_unregister(void); -#else -static inline void usb_nop_xceiv_register(void) -{ -} - -static inline void usb_nop_xceiv_unregister(void) -{ -} -#endif - -#endif /* __LINUX_USB_NOP_XCEIV_H */ diff --git a/include/linux/usb/usb_phy_gen_xceiv.h b/include/linux/usb/usb_phy_gen_xceiv.h new file mode 100644 index 000000000000..f9a7e7bc925b --- /dev/null +++ b/include/linux/usb/usb_phy_gen_xceiv.h @@ -0,0 +1,29 @@ +#ifndef __LINUX_USB_NOP_XCEIV_H +#define __LINUX_USB_NOP_XCEIV_H + +#include + +struct usb_phy_gen_xceiv_platform_data { + enum usb_phy_type type; + unsigned long clk_rate; + + /* if set fails with -EPROBE_DEFER if can't get regulator */ + unsigned int needs_vcc:1; + unsigned int needs_reset:1; +}; + +#if IS_ENABLED(CONFIG_NOP_USB_XCEIV) +/* sometimes transceivers are accessed only through e.g. ULPI */ +extern void usb_nop_xceiv_register(void); +extern void usb_nop_xceiv_unregister(void); +#else +static inline void usb_nop_xceiv_register(void) +{ +} + +static inline void usb_nop_xceiv_unregister(void) +{ +} +#endif + +#endif /* __LINUX_USB_NOP_XCEIV_H */ -- cgit v1.2.3-71-gd317 From 851cf6e7d6366195d4ee033cdc7787df1a649a14 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 9 Aug 2013 19:51:57 +0530 Subject: jump_label: Split jumplabel ratelimit Commit b202952075f62603bea9bfb6ebc6b0420db11949 ("perf, core: Rate limit perf_sched_events jump_label patching") introduced rate limiting for jump label disabling. The changes were made in the jump label code in order to be more widely available and to keep things tidier. This is all fine, except now jump_label.h includes linux/workqueue.h, which makes it impossible to include jump_label.h from anything that workqueue.h needs. For example, it's now impossible to include jump_label.h from asm/spinlock.h, which is done in proposed pv-ticketlock patches. This patch splits out the rate limiting related changes from jump_label.h into a new file, jump_label_ratelimit.h, to resolve the issue. Signed-off-by: Andrew Jones Link: http://lkml.kernel.org/r/1376058122-8248-10-git-send-email-raghavendra.kt@linux.vnet.ibm.com Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Raghavendra K T Acked-by: Ingo Molnar Signed-off-by: H. Peter Anvin --- include/linux/jump_label.h | 28 +--------------------------- include/linux/jump_label_ratelimit.h | 34 ++++++++++++++++++++++++++++++++++ include/linux/perf_event.h | 1 + kernel/jump_label.c | 1 + 4 files changed, 37 insertions(+), 27 deletions(-) create mode 100644 include/linux/jump_label_ratelimit.h (limited to 'include') diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index 0976fc46d1e0..a5079072da66 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h @@ -48,7 +48,6 @@ #include #include -#include #if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL) @@ -61,12 +60,6 @@ struct static_key { #endif }; -struct static_key_deferred { - struct static_key key; - unsigned long timeout; - struct delayed_work work; -}; - # include # define HAVE_JUMP_LABEL #endif /* CC_HAVE_ASM_GOTO && CONFIG_JUMP_LABEL */ @@ -78,6 +71,7 @@ enum jump_label_type { struct module; +#include #ifdef HAVE_JUMP_LABEL #define JUMP_LABEL_TRUE_BRANCH 1UL @@ -119,10 +113,7 @@ extern void arch_jump_label_transform_static(struct jump_entry *entry, extern int jump_label_text_reserved(void *start, void *end); extern void static_key_slow_inc(struct static_key *key); extern void static_key_slow_dec(struct static_key *key); -extern void static_key_slow_dec_deferred(struct static_key_deferred *key); extern void jump_label_apply_nops(struct module *mod); -extern void -jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl); #define STATIC_KEY_INIT_TRUE ((struct static_key) \ { .enabled = ATOMIC_INIT(1), .entries = (void *)1 }) @@ -131,8 +122,6 @@ jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl); #else /* !HAVE_JUMP_LABEL */ -#include - struct static_key { atomic_t enabled; }; @@ -141,10 +130,6 @@ static __always_inline void jump_label_init(void) { } -struct static_key_deferred { - struct static_key key; -}; - static __always_inline bool static_key_false(struct static_key *key) { if (unlikely(atomic_read(&key->enabled)) > 0) @@ -169,11 +154,6 @@ static inline void static_key_slow_dec(struct static_key *key) atomic_dec(&key->enabled); } -static inline void static_key_slow_dec_deferred(struct static_key_deferred *key) -{ - static_key_slow_dec(&key->key); -} - static inline int jump_label_text_reserved(void *start, void *end) { return 0; @@ -187,12 +167,6 @@ static inline int jump_label_apply_nops(struct module *mod) return 0; } -static inline void -jump_label_rate_limit(struct static_key_deferred *key, - unsigned long rl) -{ -} - #define STATIC_KEY_INIT_TRUE ((struct static_key) \ { .enabled = ATOMIC_INIT(1) }) #define STATIC_KEY_INIT_FALSE ((struct static_key) \ diff --git a/include/linux/jump_label_ratelimit.h b/include/linux/jump_label_ratelimit.h new file mode 100644 index 000000000000..113788389b3d --- /dev/null +++ b/include/linux/jump_label_ratelimit.h @@ -0,0 +1,34 @@ +#ifndef _LINUX_JUMP_LABEL_RATELIMIT_H +#define _LINUX_JUMP_LABEL_RATELIMIT_H + +#include +#include + +#if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL) +struct static_key_deferred { + struct static_key key; + unsigned long timeout; + struct delayed_work work; +}; +#endif + +#ifdef HAVE_JUMP_LABEL +extern void static_key_slow_dec_deferred(struct static_key_deferred *key); +extern void +jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl); + +#else /* !HAVE_JUMP_LABEL */ +struct static_key_deferred { + struct static_key key; +}; +static inline void static_key_slow_dec_deferred(struct static_key_deferred *key) +{ + static_key_slow_dec(&key->key); +} +static inline void +jump_label_rate_limit(struct static_key_deferred *key, + unsigned long rl) +{ +} +#endif /* HAVE_JUMP_LABEL */ +#endif /* _LINUX_JUMP_LABEL_RATELIMIT_H */ diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index c43f6eabad5b..226be8da3f85 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -48,6 +48,7 @@ struct perf_guest_info_callbacks { #include #include #include +#include #include #include #include diff --git a/kernel/jump_label.c b/kernel/jump_label.c index 60f48fa0fd0d..297a9247a3b3 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c @@ -13,6 +13,7 @@ #include #include #include +#include #ifdef HAVE_JUMP_LABEL -- cgit v1.2.3-71-gd317 From 3a3bb00d5c73f3ae7833f6534982b5b2f56ac9b4 Mon Sep 17 00:00:00 2001 From: Raghavendra K T Date: Fri, 9 Aug 2013 19:52:00 +0530 Subject: kvm uapi: Add KICK_CPU and PV_UNHALT definition to uapi These are needed by both guest and host. Originally-from: Srivatsa Vaddagiri Signed-off-by: Raghavendra K T Link: http://lkml.kernel.org/r/1376058122-8248-13-git-send-email-raghavendra.kt@linux.vnet.ibm.com Acked-by: Gleb Natapov Acked-by: Ingo Molnar Signed-off-by: H. Peter Anvin --- arch/x86/include/uapi/asm/kvm_para.h | 1 + include/uapi/linux/kvm_para.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/arch/x86/include/uapi/asm/kvm_para.h b/arch/x86/include/uapi/asm/kvm_para.h index 06fdbd987e97..94dc8ca434e0 100644 --- a/arch/x86/include/uapi/asm/kvm_para.h +++ b/arch/x86/include/uapi/asm/kvm_para.h @@ -23,6 +23,7 @@ #define KVM_FEATURE_ASYNC_PF 4 #define KVM_FEATURE_STEAL_TIME 5 #define KVM_FEATURE_PV_EOI 6 +#define KVM_FEATURE_PV_UNHALT 7 /* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h index cea2c5c72d26..2841f86eae0b 100644 --- a/include/uapi/linux/kvm_para.h +++ b/include/uapi/linux/kvm_para.h @@ -19,6 +19,7 @@ #define KVM_HC_MMU_OP 2 #define KVM_HC_FEATURES 3 #define KVM_HC_PPC_MAP_MAGIC_PAGE 4 +#define KVM_HC_KICK_CPU 5 /* * hypercalls use architecture specific -- cgit v1.2.3-71-gd317 From 6efa20e49b9cb1db1ab66870cc37323474a75a13 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 19 Jul 2013 11:51:31 -0400 Subject: xen: Support 64-bit PV guest receiving NMIs This is based on a patch that Zhenzhong Duan had sent - which was missing some of the remaining pieces. The kernel has the logic to handle Xen-type-exceptions using the paravirt interface in the assembler code (see PARAVIRT_ADJUST_EXCEPTION_FRAME - pv_irq_ops.adjust_exception_frame and and INTERRUPT_RETURN - pv_cpu_ops.iret). That means the nmi handler (and other exception handlers) use the hypervisor iret. The other changes that would be neccessary for this would be to translate the NMI_VECTOR to one of the entries on the ipi_vector and make xen_send_IPI_mask_allbutself use different events. Fortunately for us commit 1db01b4903639fcfaec213701a494fe3fb2c490b (xen: Clean up apic ipi interface) implemented this and we piggyback on the cleanup such that the apic IPI interface will pass the right vector value for NMI. With this patch we can trigger NMIs within a PV guest (only tested x86_64). For this to work with normal PV guests (not initial domain) we need the domain to be able to use the APIC ops - they are already implemented to use the Xen event channels. For that to be turned on in a PV domU we need to remove the masking of X86_FEATURE_APIC. Incidentally that means kgdb will also now work within a PV guest without using the 'nokgdbroundup' workaround. Note that the 32-bit version is different and this patch does not enable that. CC: Lisa Nguyen CC: Ben Guthro CC: Zhenzhong Duan Signed-off-by: Konrad Rzeszutek Wilk [v1: Fixed up per David Vrabel comments] Reviewed-by: Ben Guthro Reviewed-by: David Vrabel --- arch/x86/include/asm/xen/events.h | 1 + arch/x86/xen/enlighten.c | 13 ++++++++----- arch/x86/xen/setup.c | 13 +++++++++++-- arch/x86/xen/smp.c | 6 ++++++ drivers/xen/events.c | 11 ++++++++++- include/xen/interface/vcpu.h | 2 ++ 6 files changed, 38 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/xen/events.h b/arch/x86/include/asm/xen/events.h index ca842f2769ef..608a79d5a466 100644 --- a/arch/x86/include/asm/xen/events.h +++ b/arch/x86/include/asm/xen/events.h @@ -7,6 +7,7 @@ enum ipi_vector { XEN_CALL_FUNCTION_SINGLE_VECTOR, XEN_SPIN_UNLOCK_VECTOR, XEN_IRQ_WORK_VECTOR, + XEN_NMI_VECTOR, XEN_NR_IPIS, }; diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index 193097ef3d7d..b5a22fa7e249 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -427,8 +427,7 @@ static void __init xen_init_cpuid_mask(void) if (!xen_initial_domain()) cpuid_leaf1_edx_mask &= - ~((1 << X86_FEATURE_APIC) | /* disable local APIC */ - (1 << X86_FEATURE_ACPI)); /* disable ACPI */ + ~((1 << X86_FEATURE_ACPI)); /* disable ACPI */ cpuid_leaf1_ecx_mask &= ~(1 << (X86_FEATURE_X2APIC % 32)); @@ -735,8 +734,7 @@ static int cvt_gate_to_trap(int vector, const gate_desc *val, addr = (unsigned long)xen_int3; else if (addr == (unsigned long)stack_segment) addr = (unsigned long)xen_stack_segment; - else if (addr == (unsigned long)double_fault || - addr == (unsigned long)nmi) { + else if (addr == (unsigned long)double_fault) { /* Don't need to handle these */ return 0; #ifdef CONFIG_X86_MCE @@ -747,7 +745,12 @@ static int cvt_gate_to_trap(int vector, const gate_desc *val, */ ; #endif - } else { + } else if (addr == (unsigned long)nmi) + /* + * Use the native version as well. + */ + ; + else { /* Some other trap using IST? */ if (WARN_ON(val->ist != 0)) return 0; diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index 056d11faef21..403f5cc5415b 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c @@ -33,6 +33,9 @@ /* These are code, but not functions. Defined in entry.S */ extern const char xen_hypervisor_callback[]; extern const char xen_failsafe_callback[]; +#ifdef CONFIG_X86_64 +extern const char nmi[]; +#endif extern void xen_sysenter_target(void); extern void xen_syscall_target(void); extern void xen_syscall32_target(void); @@ -525,7 +528,13 @@ void xen_enable_syscall(void) } #endif /* CONFIG_X86_64 */ } - +void __cpuinit xen_enable_nmi(void) +{ +#ifdef CONFIG_X86_64 + if (register_callback(CALLBACKTYPE_nmi, nmi)) + BUG(); +#endif +} void __init xen_arch_setup(void) { xen_panic_handler_init(); @@ -543,7 +552,7 @@ void __init xen_arch_setup(void) xen_enable_sysenter(); xen_enable_syscall(); - + xen_enable_nmi(); #ifdef CONFIG_ACPI if (!(xen_start_info->flags & SIF_INITDOMAIN)) { printk(KERN_INFO "ACPI in unprivileged domain disabled\n"); diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index ca92754eb846..22759c6d309f 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -572,6 +572,12 @@ static inline int xen_map_vector(int vector) case IRQ_WORK_VECTOR: xen_vector = XEN_IRQ_WORK_VECTOR; break; +#ifdef CONFIG_X86_64 + case NMI_VECTOR: + case APIC_DM_NMI: /* Some use that instead of NMI_VECTOR */ + xen_vector = XEN_NMI_VECTOR; + break; +#endif default: xen_vector = -1; printk(KERN_ERR "xen: vector 0x%x is not implemented\n", diff --git a/drivers/xen/events.c b/drivers/xen/events.c index a58ac435a9a4..c8b9d9fc77b5 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -56,6 +56,7 @@ #include #include #include +#include #include /* @@ -1212,7 +1213,15 @@ EXPORT_SYMBOL_GPL(evtchn_put); void xen_send_IPI_one(unsigned int cpu, enum ipi_vector vector) { - int irq = per_cpu(ipi_to_irq, cpu)[vector]; + int irq; + + if (unlikely(vector == XEN_NMI_VECTOR)) { + int rc = HYPERVISOR_vcpu_op(VCPUOP_send_nmi, cpu, NULL); + if (rc < 0) + printk(KERN_WARNING "Sending nmi to CPU%d failed (rc:%d)\n", cpu, rc); + return; + } + irq = per_cpu(ipi_to_irq, cpu)[vector]; BUG_ON(irq < 0); notify_remote_via_irq(irq); } diff --git a/include/xen/interface/vcpu.h b/include/xen/interface/vcpu.h index 87e6f8a48661..b05288ce3991 100644 --- a/include/xen/interface/vcpu.h +++ b/include/xen/interface/vcpu.h @@ -170,4 +170,6 @@ struct vcpu_register_vcpu_info { }; DEFINE_GUEST_HANDLE_STRUCT(vcpu_register_vcpu_info); +/* Send an NMI to the specified VCPU. @extra_arg == NULL. */ +#define VCPUOP_send_nmi 11 #endif /* __XEN_PUBLIC_VCPU_H__ */ -- cgit v1.2.3-71-gd317 From e2683957fb268c6b29316fd9e7191e13239a30a5 Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Tue, 30 Jul 2013 13:29:47 -0400 Subject: drivers/tpm: add xen tpmfront interface This is a complete rewrite of the Xen TPM frontend driver, taking advantage of a simplified frontend/backend interface and adding support for cancellation and timeouts. The backend for this driver is provided by a vTPM stub domain using the interface in Xen 4.3. Signed-off-by: Daniel De Graaf Acked-by: Matthew Fioravante Reviewed-by: Konrad Rzeszutek Wilk Acked-by: Peter Huewe Reviewed-by: Peter Huewe Signed-off-by: Konrad Rzeszutek Wilk --- Documentation/tpm/xen-tpmfront.txt | 113 +++++++++ drivers/char/tpm/Kconfig | 11 + drivers/char/tpm/Makefile | 1 + drivers/char/tpm/xen-tpmfront.c | 473 +++++++++++++++++++++++++++++++++++++ include/xen/interface/io/tpmif.h | 52 ++++ 5 files changed, 650 insertions(+) create mode 100644 Documentation/tpm/xen-tpmfront.txt create mode 100644 drivers/char/tpm/xen-tpmfront.c create mode 100644 include/xen/interface/io/tpmif.h (limited to 'include') diff --git a/Documentation/tpm/xen-tpmfront.txt b/Documentation/tpm/xen-tpmfront.txt new file mode 100644 index 000000000000..69346de87ff3 --- /dev/null +++ b/Documentation/tpm/xen-tpmfront.txt @@ -0,0 +1,113 @@ +Virtual TPM interface for Xen + +Authors: Matthew Fioravante (JHUAPL), Daniel De Graaf (NSA) + +This document describes the virtual Trusted Platform Module (vTPM) subsystem for +Xen. The reader is assumed to have familiarity with building and installing Xen, +Linux, and a basic understanding of the TPM and vTPM concepts. + +INTRODUCTION + +The goal of this work is to provide a TPM functionality to a virtual guest +operating system (in Xen terms, a DomU). This allows programs to interact with +a TPM in a virtual system the same way they interact with a TPM on the physical +system. Each guest gets its own unique, emulated, software TPM. However, each +of the vTPM's secrets (Keys, NVRAM, etc) are managed by a vTPM Manager domain, +which seals the secrets to the Physical TPM. If the process of creating each of +these domains (manager, vTPM, and guest) is trusted, the vTPM subsystem extends +the chain of trust rooted in the hardware TPM to virtual machines in Xen. Each +major component of vTPM is implemented as a separate domain, providing secure +separation guaranteed by the hypervisor. The vTPM domains are implemented in +mini-os to reduce memory and processor overhead. + +This mini-os vTPM subsystem was built on top of the previous vTPM work done by +IBM and Intel corporation. + + +DESIGN OVERVIEW +--------------- + +The architecture of vTPM is described below: + ++------------------+ +| Linux DomU | ... +| | ^ | +| v | | +| xen-tpmfront | ++------------------+ + | ^ + v | ++------------------+ +| mini-os/tpmback | +| | ^ | +| v | | +| vtpm-stubdom | ... +| | ^ | +| v | | +| mini-os/tpmfront | ++------------------+ + | ^ + v | ++------------------+ +| mini-os/tpmback | +| | ^ | +| v | | +| vtpmmgr-stubdom | +| | ^ | +| v | | +| mini-os/tpm_tis | ++------------------+ + | ^ + v | ++------------------+ +| Hardware TPM | ++------------------+ + + * Linux DomU: The Linux based guest that wants to use a vTPM. There may be + more than one of these. + + * xen-tpmfront.ko: Linux kernel virtual TPM frontend driver. This driver + provides vTPM access to a Linux-based DomU. + + * mini-os/tpmback: Mini-os TPM backend driver. The Linux frontend driver + connects to this backend driver to facilitate communications + between the Linux DomU and its vTPM. This driver is also + used by vtpmmgr-stubdom to communicate with vtpm-stubdom. + + * vtpm-stubdom: A mini-os stub domain that implements a vTPM. There is a + one to one mapping between running vtpm-stubdom instances and + logical vtpms on the system. The vTPM Platform Configuration + Registers (PCRs) are normally all initialized to zero. + + * mini-os/tpmfront: Mini-os TPM frontend driver. The vTPM mini-os domain + vtpm-stubdom uses this driver to communicate with + vtpmmgr-stubdom. This driver is also used in mini-os + domains such as pv-grub that talk to the vTPM domain. + + * vtpmmgr-stubdom: A mini-os domain that implements the vTPM manager. There is + only one vTPM manager and it should be running during the + entire lifetime of the machine. This domain regulates + access to the physical TPM on the system and secures the + persistent state of each vTPM. + + * mini-os/tpm_tis: Mini-os TPM version 1.2 TPM Interface Specification (TIS) + driver. This driver used by vtpmmgr-stubdom to talk directly to + the hardware TPM. Communication is facilitated by mapping + hardware memory pages into vtpmmgr-stubdom. + + * Hardware TPM: The physical TPM that is soldered onto the motherboard. + + +INTEGRATION WITH XEN +-------------------- + +Support for the vTPM driver was added in Xen using the libxl toolstack in Xen +4.3. See the Xen documentation (docs/misc/vtpm.txt) for details on setting up +the vTPM and vTPM Manager stub domains. Once the stub domains are running, a +vTPM device is set up in the same manner as a disk or network device in the +domain's configuration file. + +In order to use features such as IMA that require a TPM to be loaded prior to +the initrd, the xen-tpmfront driver must be compiled in to the kernel. If not +using such features, the driver can be compiled as a module and will be loaded +as usual. diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index dbfd56446c31..205ed35c1a31 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -91,4 +91,15 @@ config TCG_ST33_I2C To compile this driver as a module, choose M here; the module will be called tpm_stm_st33_i2c. +config TCG_XEN + tristate "XEN TPM Interface" + depends on TCG_TPM && XEN + ---help--- + If you want to make TPM support available to a Xen user domain, + say Yes and it will be accessible from within Linux. See + the manpages for xl, xl.conf, and docs/misc/vtpm.txt in + the Xen source repository for more details. + To compile this driver as a module, choose M here; the module + will be called xen-tpmfront. + endif # TCG_TPM diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index a3736c97c65a..eb41ff97d0ad 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o obj-$(CONFIG_TCG_ST33_I2C) += tpm_i2c_stm_st33.o +obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c new file mode 100644 index 000000000000..7a7929ba2658 --- /dev/null +++ b/drivers/char/tpm/xen-tpmfront.c @@ -0,0 +1,473 @@ +/* + * Implementation of the Xen vTPM device frontend + * + * Author: Daniel De Graaf + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "tpm.h" + +struct tpm_private { + struct tpm_chip *chip; + struct xenbus_device *dev; + + struct vtpm_shared_page *shr; + + unsigned int evtchn; + int ring_ref; + domid_t backend_id; +}; + +enum status_bits { + VTPM_STATUS_RUNNING = 0x1, + VTPM_STATUS_IDLE = 0x2, + VTPM_STATUS_RESULT = 0x4, + VTPM_STATUS_CANCELED = 0x8, +}; + +static u8 vtpm_status(struct tpm_chip *chip) +{ + struct tpm_private *priv = TPM_VPRIV(chip); + switch (priv->shr->state) { + case VTPM_STATE_IDLE: + return VTPM_STATUS_IDLE | VTPM_STATUS_CANCELED; + case VTPM_STATE_FINISH: + return VTPM_STATUS_IDLE | VTPM_STATUS_RESULT; + case VTPM_STATE_SUBMIT: + case VTPM_STATE_CANCEL: /* cancel requested, not yet canceled */ + return VTPM_STATUS_RUNNING; + default: + return 0; + } +} + +static bool vtpm_req_canceled(struct tpm_chip *chip, u8 status) +{ + return status & VTPM_STATUS_CANCELED; +} + +static void vtpm_cancel(struct tpm_chip *chip) +{ + struct tpm_private *priv = TPM_VPRIV(chip); + priv->shr->state = VTPM_STATE_CANCEL; + wmb(); + notify_remote_via_evtchn(priv->evtchn); +} + +static unsigned int shr_data_offset(struct vtpm_shared_page *shr) +{ + return sizeof(*shr) + sizeof(u32) * shr->nr_extra_pages; +} + +static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct tpm_private *priv = TPM_VPRIV(chip); + struct vtpm_shared_page *shr = priv->shr; + unsigned int offset = shr_data_offset(shr); + + u32 ordinal; + unsigned long duration; + + if (offset > PAGE_SIZE) + return -EINVAL; + + if (offset + count > PAGE_SIZE) + return -EINVAL; + + /* Wait for completion of any existing command or cancellation */ + if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, chip->vendor.timeout_c, + &chip->vendor.read_queue, true) < 0) { + vtpm_cancel(chip); + return -ETIME; + } + + memcpy(offset + (u8 *)shr, buf, count); + shr->length = count; + barrier(); + shr->state = VTPM_STATE_SUBMIT; + wmb(); + notify_remote_via_evtchn(priv->evtchn); + + ordinal = be32_to_cpu(((struct tpm_input_header*)buf)->ordinal); + duration = tpm_calc_ordinal_duration(chip, ordinal); + + if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, duration, + &chip->vendor.read_queue, true) < 0) { + /* got a signal or timeout, try to cancel */ + vtpm_cancel(chip); + return -ETIME; + } + + return count; +} + +static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct tpm_private *priv = TPM_VPRIV(chip); + struct vtpm_shared_page *shr = priv->shr; + unsigned int offset = shr_data_offset(shr); + size_t length = shr->length; + + if (shr->state == VTPM_STATE_IDLE) + return -ECANCELED; + + /* In theory the wait at the end of _send makes this one unnecessary */ + if (wait_for_tpm_stat(chip, VTPM_STATUS_RESULT, chip->vendor.timeout_c, + &chip->vendor.read_queue, true) < 0) { + vtpm_cancel(chip); + return -ETIME; + } + + if (offset > PAGE_SIZE) + return -EIO; + + if (offset + length > PAGE_SIZE) + length = PAGE_SIZE - offset; + + if (length > count) + length = count; + + memcpy(buf, offset + (u8 *)shr, length); + + return length; +} + +ssize_t tpm_show_locality(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_private *priv = TPM_VPRIV(chip); + u8 locality = priv->shr->locality; + + return sprintf(buf, "%d\n", locality); +} + +ssize_t tpm_store_locality(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_private *priv = TPM_VPRIV(chip); + u8 val; + + int rv = kstrtou8(buf, 0, &val); + if (rv) + return rv; + + priv->shr->locality = val; + + return len; +} + +static const struct file_operations vtpm_ops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = tpm_open, + .read = tpm_read, + .write = tpm_write, + .release = tpm_release, +}; + +static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); +static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); +static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); +static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); +static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); +static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, + NULL); +static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL); +static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); +static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL); +static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL); +static DEVICE_ATTR(locality, S_IRUGO | S_IWUSR, tpm_show_locality, + tpm_store_locality); + +static struct attribute *vtpm_attrs[] = { + &dev_attr_pubek.attr, + &dev_attr_pcrs.attr, + &dev_attr_enabled.attr, + &dev_attr_active.attr, + &dev_attr_owned.attr, + &dev_attr_temp_deactivated.attr, + &dev_attr_caps.attr, + &dev_attr_cancel.attr, + &dev_attr_durations.attr, + &dev_attr_timeouts.attr, + &dev_attr_locality.attr, + NULL, +}; + +static struct attribute_group vtpm_attr_grp = { + .attrs = vtpm_attrs, +}; + +#define TPM_LONG_TIMEOUT (10 * 60 * HZ) + +static const struct tpm_vendor_specific tpm_vtpm = { + .status = vtpm_status, + .recv = vtpm_recv, + .send = vtpm_send, + .cancel = vtpm_cancel, + .req_complete_mask = VTPM_STATUS_IDLE | VTPM_STATUS_RESULT, + .req_complete_val = VTPM_STATUS_IDLE | VTPM_STATUS_RESULT, + .req_canceled = vtpm_req_canceled, + .attr_group = &vtpm_attr_grp, + .miscdev = { + .fops = &vtpm_ops, + }, + .duration = { + TPM_LONG_TIMEOUT, + TPM_LONG_TIMEOUT, + TPM_LONG_TIMEOUT, + }, +}; + +static irqreturn_t tpmif_interrupt(int dummy, void *dev_id) +{ + struct tpm_private *priv = dev_id; + + switch (priv->shr->state) { + case VTPM_STATE_IDLE: + case VTPM_STATE_FINISH: + wake_up_interruptible(&priv->chip->vendor.read_queue); + break; + case VTPM_STATE_SUBMIT: + case VTPM_STATE_CANCEL: + default: + break; + } + return IRQ_HANDLED; +} + +static int setup_chip(struct device *dev, struct tpm_private *priv) +{ + struct tpm_chip *chip; + + chip = tpm_register_hardware(dev, &tpm_vtpm); + if (!chip) + return -ENODEV; + + init_waitqueue_head(&chip->vendor.read_queue); + + priv->chip = chip; + TPM_VPRIV(chip) = priv; + + return 0; +} + +/* caller must clean up in case of errors */ +static int setup_ring(struct xenbus_device *dev, struct tpm_private *priv) +{ + struct xenbus_transaction xbt; + const char *message = NULL; + int rv; + + priv->shr = (void *)__get_free_page(GFP_KERNEL|__GFP_ZERO); + if (!priv->shr) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring"); + return -ENOMEM; + } + + rv = xenbus_grant_ring(dev, virt_to_mfn(priv->shr)); + if (rv < 0) + return rv; + + priv->ring_ref = rv; + + rv = xenbus_alloc_evtchn(dev, &priv->evtchn); + if (rv) + return rv; + + rv = bind_evtchn_to_irqhandler(priv->evtchn, tpmif_interrupt, 0, + "tpmif", priv); + if (rv <= 0) { + xenbus_dev_fatal(dev, rv, "allocating TPM irq"); + return rv; + } + priv->chip->vendor.irq = rv; + + again: + rv = xenbus_transaction_start(&xbt); + if (rv) { + xenbus_dev_fatal(dev, rv, "starting transaction"); + return rv; + } + + rv = xenbus_printf(xbt, dev->nodename, + "ring-ref", "%u", priv->ring_ref); + if (rv) { + message = "writing ring-ref"; + goto abort_transaction; + } + + rv = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", + priv->evtchn); + if (rv) { + message = "writing event-channel"; + goto abort_transaction; + } + + rv = xenbus_printf(xbt, dev->nodename, "feature-protocol-v2", "1"); + if (rv) { + message = "writing feature-protocol-v2"; + goto abort_transaction; + } + + rv = xenbus_transaction_end(xbt, 0); + if (rv == -EAGAIN) + goto again; + if (rv) { + xenbus_dev_fatal(dev, rv, "completing transaction"); + return rv; + } + + xenbus_switch_state(dev, XenbusStateInitialised); + + return 0; + + abort_transaction: + xenbus_transaction_end(xbt, 1); + if (message) + xenbus_dev_error(dev, rv, "%s", message); + + return rv; +} + +static void ring_free(struct tpm_private *priv) +{ + if (!priv) + return; + + if (priv->ring_ref) + gnttab_end_foreign_access(priv->ring_ref, 0, + (unsigned long)priv->shr); + else + free_page((unsigned long)priv->shr); + + if (priv->chip && priv->chip->vendor.irq) + unbind_from_irqhandler(priv->chip->vendor.irq, priv); + + kfree(priv); +} + +static int tpmfront_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + struct tpm_private *priv; + int rv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating priv structure"); + return -ENOMEM; + } + + rv = setup_chip(&dev->dev, priv); + if (rv) { + kfree(priv); + return rv; + } + + rv = setup_ring(dev, priv); + if (rv) { + tpm_remove_hardware(&dev->dev); + ring_free(priv); + return rv; + } + + tpm_get_timeouts(priv->chip); + + dev_set_drvdata(&dev->dev, priv->chip); + + return rv; +} + +static int tpmfront_remove(struct xenbus_device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(&dev->dev); + struct tpm_private *priv = TPM_VPRIV(chip); + tpm_remove_hardware(&dev->dev); + ring_free(priv); + TPM_VPRIV(chip) = NULL; + return 0; +} + +static int tpmfront_resume(struct xenbus_device *dev) +{ + /* A suspend/resume/migrate will interrupt a vTPM anyway */ + tpmfront_remove(dev); + return tpmfront_probe(dev, NULL); +} + +static void backend_changed(struct xenbus_device *dev, + enum xenbus_state backend_state) +{ + int val; + + switch (backend_state) { + case XenbusStateInitialised: + case XenbusStateConnected: + if (dev->state == XenbusStateConnected) + break; + + if (xenbus_scanf(XBT_NIL, dev->otherend, + "feature-protocol-v2", "%d", &val) < 0) + val = 0; + if (!val) { + xenbus_dev_fatal(dev, -EINVAL, + "vTPM protocol 2 required"); + return; + } + xenbus_switch_state(dev, XenbusStateConnected); + break; + + case XenbusStateClosing: + case XenbusStateClosed: + device_unregister(&dev->dev); + xenbus_frontend_closed(dev); + break; + default: + break; + } +} + +static const struct xenbus_device_id tpmfront_ids[] = { + { "vtpm" }, + { "" } +}; +MODULE_ALIAS("xen:vtpm"); + +static DEFINE_XENBUS_DRIVER(tpmfront, , + .probe = tpmfront_probe, + .remove = tpmfront_remove, + .resume = tpmfront_resume, + .otherend_changed = backend_changed, + ); + +static int __init xen_tpmfront_init(void) +{ + if (!xen_domain()) + return -ENODEV; + + return xenbus_register_frontend(&tpmfront_driver); +} +module_init(xen_tpmfront_init); + +static void __exit xen_tpmfront_exit(void) +{ + xenbus_unregister_driver(&tpmfront_driver); +} +module_exit(xen_tpmfront_exit); + +MODULE_AUTHOR("Daniel De Graaf "); +MODULE_DESCRIPTION("Xen vTPM Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/xen/interface/io/tpmif.h b/include/xen/interface/io/tpmif.h new file mode 100644 index 000000000000..28e7dcd75e82 --- /dev/null +++ b/include/xen/interface/io/tpmif.h @@ -0,0 +1,52 @@ +/****************************************************************************** + * tpmif.h + * + * TPM I/O interface for Xen guest OSes, v2 + * + * This file is in the public domain. + * + */ + +#ifndef __XEN_PUBLIC_IO_TPMIF_H__ +#define __XEN_PUBLIC_IO_TPMIF_H__ + +/* + * Xenbus state machine + * + * Device open: + * 1. Both ends start in XenbusStateInitialising + * 2. Backend transitions to InitWait (frontend does not wait on this step) + * 3. Frontend populates ring-ref, event-channel, feature-protocol-v2 + * 4. Frontend transitions to Initialised + * 5. Backend maps grant and event channel, verifies feature-protocol-v2 + * 6. Backend transitions to Connected + * 7. Frontend verifies feature-protocol-v2, transitions to Connected + * + * Device close: + * 1. State is changed to XenbusStateClosing + * 2. Frontend transitions to Closed + * 3. Backend unmaps grant and event, changes state to InitWait + */ + +enum vtpm_shared_page_state { + VTPM_STATE_IDLE, /* no contents / vTPM idle / cancel complete */ + VTPM_STATE_SUBMIT, /* request ready / vTPM working */ + VTPM_STATE_FINISH, /* response ready / vTPM idle */ + VTPM_STATE_CANCEL, /* cancel requested / vTPM working */ +}; +/* The backend should only change state to IDLE or FINISH, while the + * frontend should only change to SUBMIT or CANCEL. */ + + +struct vtpm_shared_page { + uint32_t length; /* request/response length in bytes */ + + uint8_t state; /* enum vtpm_shared_page_state */ + uint8_t locality; /* for the current request */ + uint8_t pad; + + uint8_t nr_extra_pages; /* extra pages for long packets; may be zero */ + uint32_t extra_pages[0]; /* grant IDs; length in nr_extra_pages */ +}; + +#endif -- cgit v1.2.3-71-gd317 From cd9151e26d31048b2b5e00fd02e110e07d2200c9 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Sun, 4 Aug 2013 15:39:40 +0100 Subject: xen/balloon: set a mapping for ballooned out pages Currently ballooned out pages are mapped to 0 and have INVALID_P2M_ENTRY in the p2m. These ballooned out pages are used to map foreign grants by gntdev and blkback (see alloc_xenballooned_pages). Allocate a page per cpu and map all the ballooned out pages to the corresponding mfn. Set the p2m accordingly. This way reading from a ballooned out page won't cause a kernel crash (see http://lists.xen.org/archives/html/xen-devel/2012-12/msg01154.html). Signed-off-by: Stefano Stabellini Reviewed-by: David Vrabel CC: alex@alex.org.uk CC: dcrisan@flexiant.com Signed-off-by: Konrad Rzeszutek Wilk --- drivers/xen/balloon.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++--- include/xen/balloon.h | 3 +++ 2 files changed, 69 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 2a2ef97697b2..a3dc75db135c 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -38,6 +38,7 @@ #define pr_fmt(fmt) "xen:" KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -52,6 +53,7 @@ #include #include #include +#include #include #include @@ -90,6 +92,8 @@ EXPORT_SYMBOL_GPL(balloon_stats); /* We increase/decrease in batches which fit in a page */ static xen_pfn_t frame_list[PAGE_SIZE / sizeof(unsigned long)]; +static DEFINE_PER_CPU(struct page *, balloon_scratch_page); + /* List of ballooned pages, threaded through the mem_map array. */ static LIST_HEAD(ballooned_pages); @@ -412,7 +416,8 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp) if (xen_pv_domain() && !PageHighMem(page)) { ret = HYPERVISOR_update_va_mapping( (unsigned long)__va(pfn << PAGE_SHIFT), - __pte_ma(0), 0); + pfn_pte(page_to_pfn(__get_cpu_var(balloon_scratch_page)), + PAGE_KERNEL_RO), 0); BUG_ON(ret); } #endif @@ -425,7 +430,8 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp) /* No more mappings: invalidate P2M and add to balloon. */ for (i = 0; i < nr_pages; i++) { pfn = mfn_to_pfn(frame_list[i]); - __set_phys_to_machine(pfn, INVALID_P2M_ENTRY); + __set_phys_to_machine(pfn, + pfn_to_mfn(page_to_pfn(__get_cpu_var(balloon_scratch_page)))); balloon_append(pfn_to_page(pfn)); } @@ -480,6 +486,18 @@ static void balloon_process(struct work_struct *work) mutex_unlock(&balloon_mutex); } +struct page *get_balloon_scratch_page(void) +{ + struct page *ret = get_cpu_var(balloon_scratch_page); + BUG_ON(ret == NULL); + return ret; +} + +void put_balloon_scratch_page(void) +{ + put_cpu_var(balloon_scratch_page); +} + /* Resets the Xen limit, sets new target, and kicks off processing. */ void balloon_set_new_target(unsigned long target) { @@ -573,13 +591,47 @@ static void __init balloon_add_region(unsigned long start_pfn, } } +static int __cpuinit balloon_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + int cpu = (long)hcpu; + switch (action) { + case CPU_UP_PREPARE: + if (per_cpu(balloon_scratch_page, cpu) != NULL) + break; + per_cpu(balloon_scratch_page, cpu) = alloc_page(GFP_KERNEL); + if (per_cpu(balloon_scratch_page, cpu) == NULL) { + pr_warn("Failed to allocate balloon_scratch_page for cpu %d\n", cpu); + return NOTIFY_BAD; + } + break; + default: + break; + } + return NOTIFY_OK; +} + +static struct notifier_block balloon_cpu_notifier __cpuinitdata = { + .notifier_call = balloon_cpu_notify, +}; + static int __init balloon_init(void) { - int i; + int i, cpu; if (!xen_domain()) return -ENODEV; + for_each_online_cpu(cpu) + { + per_cpu(balloon_scratch_page, cpu) = alloc_page(GFP_KERNEL); + if (per_cpu(balloon_scratch_page, cpu) == NULL) { + pr_warn("Failed to allocate balloon_scratch_page for cpu %d\n", cpu); + return -ENOMEM; + } + } + register_cpu_notifier(&balloon_cpu_notifier); + pr_info("Initialising balloon driver\n"); balloon_stats.current_pages = xen_pv_domain() @@ -616,4 +668,15 @@ static int __init balloon_init(void) subsys_initcall(balloon_init); +static int __init balloon_clear(void) +{ + int cpu; + + for_each_possible_cpu(cpu) + per_cpu(balloon_scratch_page, cpu) = NULL; + + return 0; +} +early_initcall(balloon_clear); + MODULE_LICENSE("GPL"); diff --git a/include/xen/balloon.h b/include/xen/balloon.h index cc2e1a7e44ec..a4c1c6a93691 100644 --- a/include/xen/balloon.h +++ b/include/xen/balloon.h @@ -29,6 +29,9 @@ int alloc_xenballooned_pages(int nr_pages, struct page **pages, bool highmem); void free_xenballooned_pages(int nr_pages, struct page **pages); +struct page *get_balloon_scratch_page(void); +void put_balloon_scratch_page(void); + struct device; #ifdef CONFIG_XEN_SELFBALLOONING extern int register_xen_selfballooning(struct device *dev); -- cgit v1.2.3-71-gd317 From 86f8973c1053cb03e1b1b45989a4e144e05b1735 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 8 Aug 2013 16:09:50 +0200 Subject: spi: new controller driver for efm32 SoCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/efm32-spi.txt | 34 ++ drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi-efm32.c | 517 +++++++++++++++++++++ include/linux/platform_data/efm32-spi.h | 14 + 5 files changed, 573 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/efm32-spi.txt create mode 100644 drivers/spi/spi-efm32.c create mode 100644 include/linux/platform_data/efm32-spi.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/spi/efm32-spi.txt b/Documentation/devicetree/bindings/spi/efm32-spi.txt new file mode 100644 index 000000000000..a590ca51be75 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/efm32-spi.txt @@ -0,0 +1,34 @@ +* Energy Micro EFM32 SPI + +Required properties: +- #address-cells: see spi-bus.txt +- #size-cells: see spi-bus.txt +- compatible: should be "efm32,spi" +- reg: Offset and length of the register set for the controller +- interrupts: pair specifying rx and tx irq +- clocks: phandle to the spi clock +- cs-gpios: see spi-bus.txt +- location: Value to write to the ROUTE register's LOCATION bitfield to configure the pinmux for the device, see datasheet for values. + +Example: + +spi1: spi@0x4000c400 { /* USART1 */ + #address-cells = <1>; + #size-cells = <0>; + compatible = "efm32,spi"; + reg = <0x4000c400 0x400>; + interrupts = <15 16>; + clocks = <&cmu 20>; + cs-gpios = <&gpio 51 1>; // D3 + location = <1>; + status = "ok"; + + ks8851@0 { + compatible = "ks8851"; + spi-max-frequency = <6000000>; + reg = <0>; + interrupt-parent = <&boardfpga>; + interrupts = <4>; + status = "ok"; + }; +}; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 89cbbabaff44..f84a77c816bc 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -157,6 +157,13 @@ config SPI_DAVINCI help SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules. +config SPI_EFM32 + tristate "EFM32 SPI controller" + depends on OF && ARM && (ARCH_EFM32 || COMPILE_TEST) + select SPI_BITBANG + help + Driver for the spi controller found on Energy Micro's EFM32 SoCs. + config SPI_EP93XX tristate "Cirrus Logic EP93xx SPI controller" depends on ARCH_EP93XX diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 33f9c09561e7..4a1cfb247292 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o +obj-$(CONFIG_SPI_EFM32) += spi-efm32.o obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o obj-$(CONFIG_SPI_FALCON) += spi-falcon.o obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o diff --git a/drivers/spi/spi-efm32.c b/drivers/spi/spi-efm32.c new file mode 100644 index 000000000000..ba38452f646f --- /dev/null +++ b/drivers/spi/spi-efm32.c @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2012-2013 Uwe Kleine-Koenig for Pengutronix + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "efm32-spi" + +#define MASK_VAL(mask, val) ((val << __ffs(mask)) & mask) + +#define REG_CTRL 0x00 +#define REG_CTRL_SYNC 0x0001 +#define REG_CTRL_CLKPOL 0x0100 +#define REG_CTRL_CLKPHA 0x0200 +#define REG_CTRL_MSBF 0x0400 +#define REG_CTRL_TXBIL 0x1000 + +#define REG_FRAME 0x04 +#define REG_FRAME_DATABITS__MASK 0x000f +#define REG_FRAME_DATABITS(n) ((n) - 3) + +#define REG_CMD 0x0c +#define REG_CMD_RXEN 0x0001 +#define REG_CMD_RXDIS 0x0002 +#define REG_CMD_TXEN 0x0004 +#define REG_CMD_TXDIS 0x0008 +#define REG_CMD_MASTEREN 0x0010 + +#define REG_STATUS 0x10 +#define REG_STATUS_TXENS 0x0002 +#define REG_STATUS_TXC 0x0020 +#define REG_STATUS_TXBL 0x0040 +#define REG_STATUS_RXDATAV 0x0080 + +#define REG_CLKDIV 0x14 + +#define REG_RXDATAX 0x18 +#define REG_RXDATAX_RXDATA__MASK 0x01ff +#define REG_RXDATAX_PERR 0x4000 +#define REG_RXDATAX_FERR 0x8000 + +#define REG_TXDATA 0x34 + +#define REG_IF 0x40 +#define REG_IF_TXBL 0x0002 +#define REG_IF_RXDATAV 0x0004 + +#define REG_IFS 0x44 +#define REG_IFC 0x48 +#define REG_IEN 0x4c + +#define REG_ROUTE 0x54 +#define REG_ROUTE_RXPEN 0x0001 +#define REG_ROUTE_TXPEN 0x0002 +#define REG_ROUTE_CLKPEN 0x0008 +#define REG_ROUTE_LOCATION__MASK 0x0700 +#define REG_ROUTE_LOCATION(n) MASK_VAL(REG_ROUTE_LOCATION__MASK, (n)) + +struct efm32_spi_ddata { + struct spi_bitbang bitbang; + + spinlock_t lock; + + struct clk *clk; + void __iomem *base; + unsigned int rxirq, txirq; + struct efm32_spi_pdata pdata; + + /* irq data */ + struct completion done; + const u8 *tx_buf; + u8 *rx_buf; + unsigned tx_len, rx_len; + + /* chip selects */ + unsigned csgpio[]; +}; + +#define ddata_to_dev(ddata) (&(ddata->bitbang.master->dev)) +#define efm32_spi_vdbg(ddata, format, arg...) \ + dev_vdbg(ddata_to_dev(ddata), format, ##arg) + +static void efm32_spi_write32(struct efm32_spi_ddata *ddata, + u32 value, unsigned offset) +{ + writel_relaxed(value, ddata->base + offset); +} + +static u32 efm32_spi_read32(struct efm32_spi_ddata *ddata, unsigned offset) +{ + return readl_relaxed(ddata->base + offset); +} + +static void efm32_spi_chipselect(struct spi_device *spi, int is_on) +{ + struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master); + int value = !(spi->mode & SPI_CS_HIGH) == !(is_on == BITBANG_CS_ACTIVE); + + gpio_set_value(ddata->csgpio[spi->chip_select], value); +} + +static int efm32_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master); + + unsigned bpw = t->bits_per_word ?: spi->bits_per_word; + unsigned speed = t->speed_hz ?: spi->max_speed_hz; + unsigned long clkfreq = clk_get_rate(ddata->clk); + u32 clkdiv; + + efm32_spi_write32(ddata, REG_CTRL_SYNC | REG_CTRL_MSBF | + (spi->mode & SPI_CPHA ? REG_CTRL_CLKPHA : 0) | + (spi->mode & SPI_CPOL ? REG_CTRL_CLKPOL : 0), REG_CTRL); + + efm32_spi_write32(ddata, + REG_FRAME_DATABITS(bpw), REG_FRAME); + + if (2 * speed >= clkfreq) + clkdiv = 0; + else + clkdiv = 64 * (DIV_ROUND_UP(2 * clkfreq, speed) - 4); + + if (clkdiv > (1U << 21)) + return -EINVAL; + + efm32_spi_write32(ddata, clkdiv, REG_CLKDIV); + efm32_spi_write32(ddata, REG_CMD_MASTEREN, REG_CMD); + efm32_spi_write32(ddata, REG_CMD_RXEN | REG_CMD_TXEN, REG_CMD); + + return 0; +} + +static void efm32_spi_tx_u8(struct efm32_spi_ddata *ddata) +{ + u8 val = 0; + + if (ddata->tx_buf) { + val = *ddata->tx_buf; + ddata->tx_buf++; + } + + ddata->tx_len--; + efm32_spi_write32(ddata, val, REG_TXDATA); + efm32_spi_vdbg(ddata, "%s: tx 0x%x\n", __func__, val); +} + +static void efm32_spi_rx_u8(struct efm32_spi_ddata *ddata) +{ + u32 rxdata = efm32_spi_read32(ddata, REG_RXDATAX); + efm32_spi_vdbg(ddata, "%s: rx 0x%x\n", __func__, rxdata); + + if (ddata->rx_buf) { + *ddata->rx_buf = rxdata; + ddata->rx_buf++; + } + + ddata->rx_len--; +} + +static void efm32_spi_filltx(struct efm32_spi_ddata *ddata) +{ + while (ddata->tx_len && + ddata->tx_len + 2 > ddata->rx_len && + efm32_spi_read32(ddata, REG_STATUS) & REG_STATUS_TXBL) { + efm32_spi_tx_u8(ddata); + } +} + +static int efm32_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) +{ + struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master); + int ret = -EBUSY; + + spin_lock_irq(&ddata->lock); + + if (ddata->tx_buf || ddata->rx_buf) + goto out_unlock; + + ddata->tx_buf = t->tx_buf; + ddata->rx_buf = t->rx_buf; + ddata->tx_len = ddata->rx_len = + t->len * DIV_ROUND_UP(t->bits_per_word, 8); + + efm32_spi_filltx(ddata); + + init_completion(&ddata->done); + + efm32_spi_write32(ddata, REG_IF_TXBL | REG_IF_RXDATAV, REG_IEN); + + spin_unlock_irq(&ddata->lock); + + wait_for_completion(&ddata->done); + + spin_lock_irq(&ddata->lock); + + ret = t->len - max(ddata->tx_len, ddata->rx_len); + + efm32_spi_write32(ddata, 0, REG_IEN); + ddata->tx_buf = ddata->rx_buf = NULL; + +out_unlock: + spin_unlock_irq(&ddata->lock); + + return ret; +} + +static irqreturn_t efm32_spi_rxirq(int irq, void *data) +{ + struct efm32_spi_ddata *ddata = data; + irqreturn_t ret = IRQ_NONE; + + spin_lock(&ddata->lock); + + while (ddata->rx_len > 0 && + efm32_spi_read32(ddata, REG_STATUS) & + REG_STATUS_RXDATAV) { + efm32_spi_rx_u8(ddata); + + ret = IRQ_HANDLED; + } + + if (!ddata->rx_len) { + u32 ien = efm32_spi_read32(ddata, REG_IEN); + + ien &= ~REG_IF_RXDATAV; + + efm32_spi_write32(ddata, ien, REG_IEN); + + complete(&ddata->done); + } + + spin_unlock(&ddata->lock); + + return ret; +} + +static irqreturn_t efm32_spi_txirq(int irq, void *data) +{ + struct efm32_spi_ddata *ddata = data; + + efm32_spi_vdbg(ddata, + "%s: txlen = %u, rxlen = %u, if=0x%08x, stat=0x%08x\n", + __func__, ddata->tx_len, ddata->rx_len, + efm32_spi_read32(ddata, REG_IF), + efm32_spi_read32(ddata, REG_STATUS)); + + spin_lock(&ddata->lock); + + efm32_spi_filltx(ddata); + + efm32_spi_vdbg(ddata, "%s: txlen = %u, rxlen = %u\n", + __func__, ddata->tx_len, ddata->rx_len); + + if (!ddata->tx_len) { + u32 ien = efm32_spi_read32(ddata, REG_IEN); + + ien &= ~REG_IF_TXBL; + + efm32_spi_write32(ddata, ien, REG_IEN); + efm32_spi_vdbg(ddata, "disable TXBL\n"); + } + + spin_unlock(&ddata->lock); + + return IRQ_HANDLED; +} + +static const struct efm32_spi_pdata efm32_spi_pdata_default = { + .location = 1, +}; + +static u32 efm32_spi_get_configured_location(struct efm32_spi_ddata *ddata) +{ + u32 reg = efm32_spi_read32(ddata, REG_ROUTE); + + return (reg & REG_ROUTE_LOCATION__MASK) >> __ffs(REG_ROUTE_LOCATION__MASK); +} + +static int efm32_spi_probe_dt(struct platform_device *pdev, + struct spi_master *master, struct efm32_spi_ddata *ddata) +{ + struct device_node *np = pdev->dev.of_node; + u32 location; + int ret; + + if (!np) + return 1; + + ret = of_property_read_u32(np, "location", &location); + if (!ret) { + dev_dbg(&pdev->dev, "using location %u\n", location); + } else { + /* default to location configured in hardware */ + location = efm32_spi_get_configured_location(ddata); + + dev_info(&pdev->dev, "fall back to location %u\n", location); + } + + ddata->pdata.location = location; + + /* spi core takes care about the bus number using an alias */ + master->bus_num = -1; + + return 0; +} + +static int efm32_spi_probe(struct platform_device *pdev) +{ + struct efm32_spi_ddata *ddata; + struct resource *res; + int ret; + struct spi_master *master; + struct device_node *np = pdev->dev.of_node; + unsigned int num_cs, i; + + num_cs = of_gpio_named_count(np, "cs-gpios"); + + master = spi_alloc_master(&pdev->dev, + sizeof(*ddata) + num_cs * sizeof(unsigned)); + if (!master) { + dev_dbg(&pdev->dev, + "failed to allocate spi master controller\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, master); + + master->dev.of_node = pdev->dev.of_node; + + master->num_chipselect = num_cs; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + + ddata = spi_master_get_devdata(master); + + ddata->bitbang.master = spi_master_get(master); + ddata->bitbang.chipselect = efm32_spi_chipselect; + ddata->bitbang.setup_transfer = efm32_spi_setup_transfer; + ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs; + + spin_lock_init(&ddata->lock); + + ddata->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(ddata->clk)) { + ret = PTR_ERR(ddata->clk); + dev_err(&pdev->dev, "failed to get clock: %d\n", ret); + goto err; + } + + for (i = 0; i < num_cs; ++i) { + ret = of_get_named_gpio(np, "cs-gpios", i); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get csgpio#%u (%d)\n", + i, ret); + goto err; + } + ddata->csgpio[i] = ret; + dev_dbg(&pdev->dev, "csgpio#%u = %u\n", i, ddata->csgpio[i]); + ret = devm_gpio_request_one(&pdev->dev, ddata->csgpio[i], + GPIOF_OUT_INIT_LOW, DRIVER_NAME); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to configure csgpio#%u (%d)\n", + i, ret); + goto err; + } + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + dev_err(&pdev->dev, "failed to determine base address\n"); + goto err; + } + + if (resource_size(res) < 60) { + ret = -EINVAL; + dev_err(&pdev->dev, "memory resource too small\n"); + goto err; + } + + ddata->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ddata->base)) { + ret = PTR_ERR(ddata->base); + dev_err(&pdev->dev, "failed to remap memory\n"); + goto err; + } + + ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "failed to get rx irq (%d)\n", ret); + goto err; + } + + ddata->rxirq = ret; + + ret = platform_get_irq(pdev, 1); + if (ret <= 0) + ret = ddata->rxirq + 1; + + ddata->txirq = ret; + + ret = clk_prepare_enable(ddata->clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable clock (%d)\n", ret); + goto err; + } + + ret = efm32_spi_probe_dt(pdev, master, ddata); + if (ret > 0) { + /* not created by device tree */ + const struct efm32_spi_pdata *pdata = + dev_get_platdata(&pdev->dev); + + if (pdata) + ddata->pdata = *pdata; + else + ddata->pdata.location = + efm32_spi_get_configured_location(ddata); + + master->bus_num = pdev->id; + + } else if (ret < 0) { + goto err_disable_clk; + } + + efm32_spi_write32(ddata, 0, REG_IEN); + efm32_spi_write32(ddata, REG_ROUTE_TXPEN | REG_ROUTE_RXPEN | + REG_ROUTE_CLKPEN | + REG_ROUTE_LOCATION(ddata->pdata.location), REG_ROUTE); + + ret = request_irq(ddata->rxirq, efm32_spi_rxirq, + 0, DRIVER_NAME " rx", ddata); + if (ret) { + dev_err(&pdev->dev, "failed to register rxirq (%d)\n", ret); + goto err_disable_clk; + } + + ret = request_irq(ddata->txirq, efm32_spi_txirq, + 0, DRIVER_NAME " tx", ddata); + if (ret) { + dev_err(&pdev->dev, "failed to register txirq (%d)\n", ret); + goto err_free_rx_irq; + } + + ret = spi_bitbang_start(&ddata->bitbang); + if (ret) { + dev_err(&pdev->dev, "spi_bitbang_start failed (%d)\n", ret); + + free_irq(ddata->txirq, ddata); +err_free_rx_irq: + free_irq(ddata->rxirq, ddata); +err_disable_clk: + clk_disable_unprepare(ddata->clk); +err: + spi_master_put(master); + kfree(master); + } + + return ret; +} + +static int efm32_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct efm32_spi_ddata *ddata = spi_master_get_devdata(master); + + efm32_spi_write32(ddata, 0, REG_IEN); + + free_irq(ddata->txirq, ddata); + free_irq(ddata->rxirq, ddata); + clk_disable_unprepare(ddata->clk); + spi_master_put(master); + kfree(master); + + return 0; +} + +static const struct of_device_id efm32_spi_dt_ids[] = { + { + .compatible = "efm32,spi", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, efm32_uart_dt_ids); + +static struct platform_driver efm32_spi_driver = { + .probe = efm32_spi_probe, + .remove = efm32_spi_remove, + + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = efm32_spi_dt_ids, + }, +}; +module_platform_driver(efm32_spi_driver); + +MODULE_AUTHOR("Uwe Kleine-Koenig "); +MODULE_DESCRIPTION("EFM32 SPI driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/include/linux/platform_data/efm32-spi.h b/include/linux/platform_data/efm32-spi.h new file mode 100644 index 000000000000..31b19ca1d73a --- /dev/null +++ b/include/linux/platform_data/efm32-spi.h @@ -0,0 +1,14 @@ +#ifndef __LINUX_PLATFORM_DATA_EFM32_SPI_H__ +#define __LINUX_PLATFORM_DATA_EFM32_SPI_H__ + +#include + +/** + * struct efm32_spi_pdata + * @location: pinmux location for the I/O pins (to be written to the ROUTE + * register) + */ +struct efm32_spi_pdata { + u8 location; +}; +#endif /* ifndef __LINUX_PLATFORM_DATA_EFM32_SPI_H__ */ -- cgit v1.2.3-71-gd317 From 5c6fe01c1fe3140a8fb088d2a9c32548b731c35e Mon Sep 17 00:00:00 2001 From: William Manley Date: Tue, 6 Aug 2013 19:03:14 +0100 Subject: net: igmp: Don't flush routing cache when force_igmp_version is modified The procfs knob /proc/sys/net/ipv4/conf/*/force_igmp_version allows the IGMP protocol version to use to be explicitly set. As a side effect this caused the routing cache to be flushed as it was declared as a DEVINET_SYSCTL_FLUSHING_ENTRY. Flushing is unnecessary and this patch makes it so flushing does not occur. Requested by Hannes Frederic Sowa as he was reviewing other patches adding procfs entries. Suggested-by: Hannes Frederic Sowa Signed-off-by: William Manley Acked-by: Hannes Frederic Sowa Acked-by: Benjamin LaHaise Signed-off-by: David S. Miller --- include/linux/inetdevice.h | 2 +- net/ipv4/devinet.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index b99cd23f3474..0a4a6cb35338 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -27,9 +27,9 @@ enum IPV4_DEVCONF_TAG, IPV4_DEVCONF_ARPFILTER, IPV4_DEVCONF_MEDIUM_ID, + IPV4_DEVCONF_FORCE_IGMP_VERSION, IPV4_DEVCONF_NOXFRM, IPV4_DEVCONF_NOPOLICY, - IPV4_DEVCONF_FORCE_IGMP_VERSION, IPV4_DEVCONF_ARP_ANNOUNCE, IPV4_DEVCONF_ARP_IGNORE, IPV4_DEVCONF_PROMOTE_SECONDARIES, diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 43923dc77440..87d47ce9fa33 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -2094,11 +2094,11 @@ static struct devinet_sysctl_table { DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), + DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, + "force_igmp_version"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), - DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION, - "force_igmp_version"), DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, "promote_secondaries"), DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, -- cgit v1.2.3-71-gd317 From 2690048c01f32bf45d1c1e1ab3079bc10ad2aea7 Mon Sep 17 00:00:00 2001 From: William Manley Date: Tue, 6 Aug 2013 19:03:15 +0100 Subject: net: igmp: Allow user-space configuration of igmp unsolicited report interval Adds the new procfs knobs: /proc/sys/net/ipv4/conf/*/igmpv2_unsolicited_report_interval /proc/sys/net/ipv4/conf/*/igmpv3_unsolicited_report_interval Which will allow userspace configuration of the IGMP unsolicited report interval (see below) in milliseconds. The defaults are 10000ms for IGMPv2 and 1000ms for IGMPv3 in accordance with RFC2236 and RFC3376. Background: If an IGMP join packet is lost you will not receive data sent to the multicast group so if no data arrives from that multicast group in a period of time after the IGMP join a second IGMP join will be sent. The delay between joins is the "IGMP Unsolicited Report Interval". Prior to this patch this value was hard coded in the kernel to 10s for IGMPv2 and 1s for IGMPv3. 10s is unsuitable for some use-cases, such as IPTV as it can cause channel change to be slow in the presence of packet loss. This patch allows the value to be overridden from userspace for both IGMPv2 and IGMPv3 such that it can be tuned accoding to the network. Tested with Wireshark and a simple program to join a (non-existent) multicast group. The distribution of timings for the second join differ based upon setting the procfs knobs. igmpvX_unsolicited_report_interval is intended to follow the pattern established by force_igmp_version, and while a procfs entry has been added a corresponding sysctl knob has not as it is my understanding that sysctl is deprecated[1]. [1]: http://lwn.net/Articles/247243/ Signed-off-by: William Manley Acked-by: Hannes Frederic Sowa Acked-by: Benjamin LaHaise Signed-off-by: David S. Miller --- include/linux/inetdevice.h | 2 ++ net/ipv4/devinet.c | 8 ++++++++ net/ipv4/igmp.c | 19 +++++++++++++++++-- 3 files changed, 27 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 0a4a6cb35338..c796ce26c7c0 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -28,6 +28,8 @@ enum IPV4_DEVCONF_ARPFILTER, IPV4_DEVCONF_MEDIUM_ID, IPV4_DEVCONF_FORCE_IGMP_VERSION, + IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL, + IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL, IPV4_DEVCONF_NOXFRM, IPV4_DEVCONF_NOPOLICY, IPV4_DEVCONF_ARP_ANNOUNCE, diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 87d47ce9fa33..a1b5bcbd04ae 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -73,6 +73,8 @@ static struct ipv4_devconf ipv4_devconf = { [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, + [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, + [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, }, }; @@ -83,6 +85,8 @@ static struct ipv4_devconf ipv4_devconf_dflt = { [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, + [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, + [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, }, }; @@ -2096,6 +2100,10 @@ static struct devinet_sysctl_table { DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, "force_igmp_version"), + DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL, + "igmpv2_unsolicited_report_interval"), + DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL, + "igmpv3_unsolicited_report_interval"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index ceb19ab9cf9d..d6c0e64ec97f 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -142,10 +142,25 @@ static int unsolicited_report_interval(struct in_device *in_dev) { + int interval_ms, interval_jiffies; + if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) - return IGMP_V2_Unsolicited_Report_Interval; + interval_ms = IN_DEV_CONF_GET( + in_dev, + IGMPV2_UNSOLICITED_REPORT_INTERVAL); else /* v3 */ - return IGMP_V3_Unsolicited_Report_Interval; + interval_ms = IN_DEV_CONF_GET( + in_dev, + IGMPV3_UNSOLICITED_REPORT_INTERVAL); + + interval_jiffies = msecs_to_jiffies(interval_ms); + + /* _timer functions can't handle a delay of 0 jiffies so ensure + * we always return a positive value. + */ + if (interval_jiffies <= 0) + interval_jiffies = 1; + return interval_jiffies; } static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im); -- cgit v1.2.3-71-gd317 From cda5f98e36576596b9230483ec52bff3cc97eb21 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 6 Aug 2013 21:18:12 +0200 Subject: net: sctp: convert sctp_checksum_disable module param into sctp sysctl Get rid of the last module parameter for SCTP and make this configurable via sysctl for SCTP like all the rest of SCTP's configuration knobs. Signed-off-by: Daniel Borkmann Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 8 ++++++++ include/net/netns/sctp.h | 3 +++ include/net/sctp/structs.h | 5 ----- net/sctp/input.c | 4 ++-- net/sctp/output.c | 5 ++++- net/sctp/protocol.c | 5 +++-- net/sctp/sysctl.c | 10 +++++++++- 7 files changed, 29 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 36be26b2ef7a..1b27b0b8687f 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1507,6 +1507,14 @@ sack_timeout - INTEGER Default: 200 +checksum_disable - BOOLEAN + Disable SCTP checksum computing and verification for debugging purpose. + + 1: Disable checksumming + 0: Enable checksumming + + Default: 0 + valid_cookie_life - INTEGER The default lifetime of the SCTP cookie (in milliseconds). The cookie is used during association establishment. diff --git a/include/net/netns/sctp.h b/include/net/netns/sctp.h index 3573a81815ad..ebfdf1e7d402 100644 --- a/include/net/netns/sctp.h +++ b/include/net/netns/sctp.h @@ -129,6 +129,9 @@ struct netns_sctp { /* Threshold for autoclose timeout, in seconds. */ unsigned long max_autoclose; + + /* Flag to disable SCTP checksumming. */ + int checksum_disable; }; #endif /* __NETNS_SCTP_H__ */ diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index d9c93a77b1a9..06ebeaaaa9aa 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -141,10 +141,6 @@ extern struct sctp_globals { /* This is the sctp port control hash. */ int port_hashsize; struct sctp_bind_hashbucket *port_hashtable; - - /* Flag to indicate whether computing and verifying checksum - * is disabled. */ - bool checksum_disable; } sctp_globals; #define sctp_max_instreams (sctp_globals.max_instreams) @@ -156,7 +152,6 @@ extern struct sctp_globals { #define sctp_assoc_hashtable (sctp_globals.assoc_hashtable) #define sctp_port_hashsize (sctp_globals.port_hashsize) #define sctp_port_hashtable (sctp_globals.port_hashtable) -#define sctp_checksum_disable (sctp_globals.checksum_disable) /* SCTP Socket type: UDP or TCP style. */ typedef enum { diff --git a/net/sctp/input.c b/net/sctp/input.c index fa91aff02388..b9a25e18bb2b 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -140,8 +140,8 @@ int sctp_rcv(struct sk_buff *skb) __skb_pull(skb, skb_transport_offset(skb)); if (skb->len < sizeof(struct sctphdr)) goto discard_it; - if (!sctp_checksum_disable && !skb_csum_unnecessary(skb) && - sctp_rcv_checksum(net, skb) < 0) + if (!net->sctp.checksum_disable && !skb_csum_unnecessary(skb) && + sctp_rcv_checksum(net, skb) < 0) goto discard_it; skb_pull(skb, sizeof(struct sctphdr)); diff --git a/net/sctp/output.c b/net/sctp/output.c index 5a55c55d71ad..cdb5f4914e17 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -395,6 +395,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) int padding; /* How much padding do we need? */ __u8 has_data = 0; struct dst_entry *dst = tp->dst; + struct net *net; unsigned char *auth = NULL; /* pointer to auth in skb data */ __u32 cksum_buf_len = sizeof(struct sctphdr); @@ -541,7 +542,9 @@ int sctp_packet_transmit(struct sctp_packet *packet) * Note: Adler-32 is no longer applicable, as has been replaced * by CRC32-C as described in . */ - if (!sctp_checksum_disable) { + net = dev_net(dst->dev); + + if (!net->sctp.checksum_disable) { if (!(dst->dev->features & NETIF_F_SCTP_CSUM)) { __u32 crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index b52ec2510101..a570a6365f87 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1193,6 +1193,9 @@ static int __net_init sctp_net_init(struct net *net) /* Whether Cookie Preservative is enabled(1) or not(0) */ net->sctp.cookie_preserve_enable = 1; + /* Whether SCTP checksumming is disabled(1) or not(0) */ + net->sctp.checksum_disable = 0; + /* Default sctp sockets to use md5 as their hmac alg */ #if defined (CONFIG_SCTP_DEFAULT_COOKIE_HMAC_MD5) net->sctp.sctp_hmac_alg = "md5"; @@ -1549,6 +1552,4 @@ MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-132"); MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-132"); MODULE_AUTHOR("Linux Kernel SCTP developers "); MODULE_DESCRIPTION("Support for the SCTP protocol (RFC2960)"); -module_param_named(no_checksums, sctp_checksum_disable, bool, 0644); -MODULE_PARM_DESC(no_checksums, "Disable checksums computing and verification"); MODULE_LICENSE("GPL"); diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 190674702b20..754809a7183d 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -296,7 +296,15 @@ static struct ctl_table sctp_net_table[] = { .extra1 = &max_autoclose_min, .extra2 = &max_autoclose_max, }, - + { + .procname = "checksum_disable", + .data = &init_net.sctp.checksum_disable, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, { /* sentinel */ } }; -- cgit v1.2.3-71-gd317 From 477143e3fece3dc12629bb1ebd7b47e8e6e72b2b Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 6 Aug 2013 21:18:13 +0200 Subject: net: sctp: trivial: update bug report in header comment With the restructuring of the lksctp.org site, we only allow bug reports through the SCTP mailing list linux-sctp@vger.kernel.org, not via SF, as SF is only used for web hosting and nothing more. While at it, also remove the obvious statement that bugs will be fixed and incooperated into the kernel. Signed-off-by: Daniel Borkmann Acked-by: Vlad Yasevich Signed-off-by: David S. Miller --- include/net/sctp/auth.h | 6 ------ include/net/sctp/checksum.h | 6 ------ include/net/sctp/command.h | 18 ++++++++---------- include/net/sctp/constants.h | 6 ------ include/net/sctp/sctp.h | 6 ------ include/net/sctp/sm.h | 6 ------ include/net/sctp/structs.h | 6 ------ include/net/sctp/tsnmap.h | 6 ------ include/net/sctp/ulpevent.h | 6 ------ include/net/sctp/ulpqueue.h | 6 ------ net/sctp/associola.c | 6 ------ net/sctp/auth.c | 6 ------ net/sctp/bind_addr.c | 6 ------ net/sctp/chunk.c | 6 ------ net/sctp/command.c | 6 ------ net/sctp/debug.c | 6 ------ net/sctp/endpointola.c | 6 ------ net/sctp/input.c | 6 ------ net/sctp/inqueue.c | 6 ------ net/sctp/ipv6.c | 6 ------ net/sctp/objcnt.c | 6 ------ net/sctp/output.c | 6 ------ net/sctp/outqueue.c | 6 ------ net/sctp/primitive.c | 6 ------ net/sctp/proc.c | 6 ------ net/sctp/protocol.c | 6 ------ net/sctp/sm_make_chunk.c | 6 ------ net/sctp/sm_sideeffect.c | 6 ------ net/sctp/sm_statefuns.c | 6 ------ net/sctp/sm_statetable.c | 6 ------ net/sctp/socket.c | 6 ------ net/sctp/ssnmap.c | 6 ------ net/sctp/sysctl.c | 6 ------ net/sctp/transport.c | 6 ------ net/sctp/tsnmap.c | 6 ------ net/sctp/ulpevent.c | 6 ------ net/sctp/ulpqueue.c | 6 ------ 37 files changed, 8 insertions(+), 226 deletions(-) (limited to 'include') diff --git a/include/net/sctp/auth.h b/include/net/sctp/auth.h index 754c511f4cf9..aa80bef3c9d5 100644 --- a/include/net/sctp/auth.h +++ b/include/net/sctp/auth.h @@ -24,14 +24,8 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Vlad Yasevich - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #ifndef __sctp_auth_h__ diff --git a/include/net/sctp/checksum.h b/include/net/sctp/checksum.h index 483e6303458d..259924d63ba6 100644 --- a/include/net/sctp/checksum.h +++ b/include/net/sctp/checksum.h @@ -27,9 +27,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Dinakaran Joseph * Jon Grimm @@ -37,9 +34,6 @@ * * Rewritten to use libcrc32c by: * Vlad Yasevich - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #ifndef __sctp_checksum_h__ diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 35247271e557..832f2191489c 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -23,19 +23,17 @@ * the Free Software Foundation, 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * - * Please send any bug reports or fixes you make to one of the - * following email addresses: + * Please send any bug reports or fixes you make to the + * email address(es): + * lksctp developers * - * La Monte H.P. Yarroll - * Karl Knutson - * Ardelle Fan - * Sridhar Samudrala - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. + * Written or modified by: + * La Monte H.P. Yarroll + * Karl Knutson + * Ardelle Fan + * Sridhar Samudrala */ - #ifndef __net_sctp_command_h__ #define __net_sctp_command_h__ diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index a1e7aa5c8bbf..2f0a565a0fd5 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -27,9 +27,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson @@ -39,9 +36,6 @@ * Xingang Guo * Sridhar Samudrala * Daisy Chang - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #ifndef __sctp_constants_h__ diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index cb28df9c2781..3794c5ad20fe 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -29,9 +29,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Xingang Guo @@ -41,9 +38,6 @@ * Ardelle Fan * Ryan Layer * Kevin Gao - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #ifndef __net_sctp_h__ diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 2aa66dda4a5a..4ef75af340b6 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -29,9 +29,6 @@ * email addresses: * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson @@ -42,9 +39,6 @@ * Daisy Chang * Ardelle Fan * Kevin Gao - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 06ebeaaaa9aa..78eedf0cd4b3 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -27,9 +27,6 @@ * email addresses: * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Randall Stewart * Ken Morneau @@ -46,9 +43,6 @@ * Ryan Layer * Anup Pemmaiah * Kevin Gao - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #ifndef __sctp_structs_h__ diff --git a/include/net/sctp/tsnmap.h b/include/net/sctp/tsnmap.h index c361d6f7e0cb..54bbbe547303 100644 --- a/include/net/sctp/tsnmap.h +++ b/include/net/sctp/tsnmap.h @@ -30,17 +30,11 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Jon Grimm * La Monte H.P. Yarroll * Karl Knutson * Sridhar Samudrala - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h index 34f0d34d22ce..27b9f5c90153 100644 --- a/include/net/sctp/ulpevent.h +++ b/include/net/sctp/ulpevent.h @@ -33,17 +33,11 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Jon Grimm * La Monte H.P. Yarroll * Karl Knutson * Sridhar Samudrala - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #ifndef __sctp_ulpevent_h__ diff --git a/include/net/sctp/ulpqueue.h b/include/net/sctp/ulpqueue.h index add3237a9fdb..b0cf5d54d717 100644 --- a/include/net/sctp/ulpqueue.h +++ b/include/net/sctp/ulpqueue.h @@ -32,16 +32,10 @@ * email addresses: * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Jon Grimm * La Monte H.P. Yarroll * Sridhar Samudrala - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #ifndef __sctp_ulpqueue_h__ diff --git a/net/sctp/associola.c b/net/sctp/associola.c index e425ba0bd79e..e051920816e6 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -30,9 +30,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson @@ -43,9 +40,6 @@ * Daisy Chang * Ryan Layer * Kevin Gao - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/auth.c b/net/sctp/auth.c index 3aab967081be..8c4fa5dec824 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -24,14 +24,8 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Vlad Yasevich - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index f34ce8bc1395..077bb070052b 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -29,17 +29,11 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson * Jon Grimm * Daisy Chang - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index b50b90c16a86..bd0bdd0ba8f1 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -26,15 +26,9 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Jon Grimm * Sridhar Samudrala - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/command.c b/net/sctp/command.c index f4bebdadf007..3d9a9ff69c03 100644 --- a/net/sctp/command.c +++ b/net/sctp/command.c @@ -27,15 +27,9 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/net/sctp/debug.c b/net/sctp/debug.c index 44aa4e7543a9..e89015d8935a 100644 --- a/net/sctp/debug.c +++ b/net/sctp/debug.c @@ -30,9 +30,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson @@ -40,9 +37,6 @@ * Jon Grimm * Daisy Chang * Sridhar Samudrala - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 825b7543a436..09b8daac87c8 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -31,18 +31,12 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson * Jon Grimm * Daisy Chang * Dajiang Zhang - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/net/sctp/input.c b/net/sctp/input.c index b9a25e18bb2b..2873e221e187 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -31,9 +31,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson @@ -43,9 +40,6 @@ * Daisy Chang * Sridhar Samudrala * Ardelle Fan - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c index e70f60ae92ef..5856932fdc38 100644 --- a/net/sctp/inqueue.c +++ b/net/sctp/inqueue.c @@ -32,15 +32,9 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 5a9402e3c0cf..da613ceae28c 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -29,9 +29,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Le Yanqun * Hui Huang @@ -42,9 +39,6 @@ * * Based on: * linux/net/ipv6/tcp_ipv6.c - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/objcnt.c b/net/sctp/objcnt.c index aec346cbed67..5ea573b37648 100644 --- a/net/sctp/objcnt.c +++ b/net/sctp/objcnt.c @@ -28,14 +28,8 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Jon Grimm - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/output.c b/net/sctp/output.c index cdb5f4914e17..e35b84cc1d9f 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -28,17 +28,11 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson * Jon Grimm * Sridhar Samudrala - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 51313233e635..94df75877869 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -30,9 +30,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson @@ -41,9 +38,6 @@ * Hui Huang * Sridhar Samudrala * Jon Grimm - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/primitive.c b/net/sctp/primitive.c index 31fa437da117..ce1ffd811775 100644 --- a/net/sctp/primitive.c +++ b/net/sctp/primitive.c @@ -31,18 +31,12 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Narasimha Budihal * Karl Knutson * Ardelle Fan * Kevin Gao - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/net/sctp/proc.c b/net/sctp/proc.c index aff0cac45b64..82432bfb742f 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -24,14 +24,8 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Sridhar Samudrala - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index a570a6365f87..54482977a48f 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -31,9 +31,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson @@ -41,9 +38,6 @@ * Sridhar Samudrala * Daisy Chang * Ardelle Fan - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 780a2d4b98a2..9c131338ccfa 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -31,9 +31,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson @@ -45,9 +42,6 @@ * Daisy Chang * Ardelle Fan * Kevin Gao - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index f1f3aac3f3bb..666c66842799 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -30,9 +30,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson @@ -42,9 +39,6 @@ * Daisy Chang * Sridhar Samudrala * Ardelle Fan - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 93271f0966c6..dfe3f36ff2aa 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -30,9 +30,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson @@ -45,9 +42,6 @@ * Ardelle Fan * Ryan Layer * Kevin Gao - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index 64ea5fd87eb2..c5999b2dde7d 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -30,9 +30,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson @@ -41,9 +38,6 @@ * Daisy Chang * Ardelle Fan * Sridhar Samudrala - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 02457123bdee..d5d5882a2891 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -36,9 +36,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Narasimha Budihal @@ -52,9 +49,6 @@ * Ryan Layer * Anup Pemmaiah * Kevin Gao - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/ssnmap.c b/net/sctp/ssnmap.c index 72b5939101bb..6007124aefa0 100644 --- a/net/sctp/ssnmap.c +++ b/net/sctp/ssnmap.c @@ -26,14 +26,8 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Jon Grimm - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 754809a7183d..1b1ee769b5bf 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -27,18 +27,12 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Mingqin Liu * Jon Grimm * Ardelle Fan * Ryan Layer * Sridhar Samudrala - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 9602c52aa617..6836ead5c957 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -32,9 +32,6 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Karl Knutson @@ -43,9 +40,6 @@ * Hui Huang * Sridhar Samudrala * Ardelle Fan - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/net/sctp/tsnmap.c b/net/sctp/tsnmap.c index 0eff8667d4c7..fbda20028285 100644 --- a/net/sctp/tsnmap.c +++ b/net/sctp/tsnmap.c @@ -29,17 +29,11 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * La Monte H.P. Yarroll * Jon Grimm * Karl Knutson * Sridhar Samudrala - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index c07624fca7e5..81089ed65456 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -30,17 +30,11 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Jon Grimm * La Monte H.P. Yarroll * Ardelle Fan * Sridhar Samudrala - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index 2cdb3010f50f..1c1484ed605d 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -29,16 +29,10 @@ * email address(es): * lksctp developers * - * Or submit a bug report through the following website: - * http://www.sf.net/projects/lksctp - * * Written or modified by: * Jon Grimm * La Monte H.P. Yarroll * Sridhar Samudrala - * - * Any bugs reported given to us we will try to fix... any fixes shared will - * be incorporated into the next SCTP release. */ #include -- cgit v1.2.3-71-gd317 From 71acc0ddd499cc323199fb1ae350ce9ea0744352 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 9 Aug 2013 13:09:41 -0700 Subject: Revert "net: sctp: convert sctp_checksum_disable module param into sctp sysctl" This reverts commit cda5f98e36576596b9230483ec52bff3cc97eb21. As per Vlad's request. Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 8 -------- include/net/netns/sctp.h | 3 --- include/net/sctp/structs.h | 5 +++++ net/sctp/input.c | 4 ++-- net/sctp/output.c | 5 +---- net/sctp/protocol.c | 5 ++--- net/sctp/sysctl.c | 10 +--------- 7 files changed, 11 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 1b27b0b8687f..36be26b2ef7a 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1507,14 +1507,6 @@ sack_timeout - INTEGER Default: 200 -checksum_disable - BOOLEAN - Disable SCTP checksum computing and verification for debugging purpose. - - 1: Disable checksumming - 0: Enable checksumming - - Default: 0 - valid_cookie_life - INTEGER The default lifetime of the SCTP cookie (in milliseconds). The cookie is used during association establishment. diff --git a/include/net/netns/sctp.h b/include/net/netns/sctp.h index ebfdf1e7d402..3573a81815ad 100644 --- a/include/net/netns/sctp.h +++ b/include/net/netns/sctp.h @@ -129,9 +129,6 @@ struct netns_sctp { /* Threshold for autoclose timeout, in seconds. */ unsigned long max_autoclose; - - /* Flag to disable SCTP checksumming. */ - int checksum_disable; }; #endif /* __NETNS_SCTP_H__ */ diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 78eedf0cd4b3..422db6cc3214 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -135,6 +135,10 @@ extern struct sctp_globals { /* This is the sctp port control hash. */ int port_hashsize; struct sctp_bind_hashbucket *port_hashtable; + + /* Flag to indicate whether computing and verifying checksum + * is disabled. */ + bool checksum_disable; } sctp_globals; #define sctp_max_instreams (sctp_globals.max_instreams) @@ -146,6 +150,7 @@ extern struct sctp_globals { #define sctp_assoc_hashtable (sctp_globals.assoc_hashtable) #define sctp_port_hashsize (sctp_globals.port_hashsize) #define sctp_port_hashtable (sctp_globals.port_hashtable) +#define sctp_checksum_disable (sctp_globals.checksum_disable) /* SCTP Socket type: UDP or TCP style. */ typedef enum { diff --git a/net/sctp/input.c b/net/sctp/input.c index 2873e221e187..5f2068679f83 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -134,8 +134,8 @@ int sctp_rcv(struct sk_buff *skb) __skb_pull(skb, skb_transport_offset(skb)); if (skb->len < sizeof(struct sctphdr)) goto discard_it; - if (!net->sctp.checksum_disable && !skb_csum_unnecessary(skb) && - sctp_rcv_checksum(net, skb) < 0) + if (!sctp_checksum_disable && !skb_csum_unnecessary(skb) && + sctp_rcv_checksum(net, skb) < 0) goto discard_it; skb_pull(skb, sizeof(struct sctphdr)); diff --git a/net/sctp/output.c b/net/sctp/output.c index e35b84cc1d9f..0ac3a65daccb 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -389,7 +389,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) int padding; /* How much padding do we need? */ __u8 has_data = 0; struct dst_entry *dst = tp->dst; - struct net *net; unsigned char *auth = NULL; /* pointer to auth in skb data */ __u32 cksum_buf_len = sizeof(struct sctphdr); @@ -536,9 +535,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) * Note: Adler-32 is no longer applicable, as has been replaced * by CRC32-C as described in . */ - net = dev_net(dst->dev); - - if (!net->sctp.checksum_disable) { + if (!sctp_checksum_disable) { if (!(dst->dev->features & NETIF_F_SCTP_CSUM)) { __u32 crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 54482977a48f..5e17092f4ada 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1187,9 +1187,6 @@ static int __net_init sctp_net_init(struct net *net) /* Whether Cookie Preservative is enabled(1) or not(0) */ net->sctp.cookie_preserve_enable = 1; - /* Whether SCTP checksumming is disabled(1) or not(0) */ - net->sctp.checksum_disable = 0; - /* Default sctp sockets to use md5 as their hmac alg */ #if defined (CONFIG_SCTP_DEFAULT_COOKIE_HMAC_MD5) net->sctp.sctp_hmac_alg = "md5"; @@ -1546,4 +1543,6 @@ MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-132"); MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-132"); MODULE_AUTHOR("Linux Kernel SCTP developers "); MODULE_DESCRIPTION("Support for the SCTP protocol (RFC2960)"); +module_param_named(no_checksums, sctp_checksum_disable, bool, 0644); +MODULE_PARM_DESC(no_checksums, "Disable checksums computing and verification"); MODULE_LICENSE("GPL"); diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 1b1ee769b5bf..6b36561a1b3b 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -290,15 +290,7 @@ static struct ctl_table sctp_net_table[] = { .extra1 = &max_autoclose_min, .extra2 = &max_autoclose_max, }, - { - .procname = "checksum_disable", - .data = &init_net.sctp.checksum_disable, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, - .extra2 = &one, - }, + { /* sentinel */ } }; -- cgit v1.2.3-71-gd317 From 9194731c5f9b2664c882a515b3398a29384a6864 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sun, 16 Jun 2013 16:25:35 +0200 Subject: drm/rcar-du: Rename platform data fields to match what they describe The struct rcar_du_encoder_data encoder::field describes the encoder type, and the rcar_du_encoder_lvds_data and rcar_du_encoder_vga_data structures describe connector properties. Rename them accordingly. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 2 +- drivers/gpu/drm/rcar-du/rcar_du_encoder.h | 3 ++- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 5 ++--- include/linux/platform_data/rcar-du.h | 19 +++++++++++++------ 4 files changed, 18 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 15a56433c80c..0d0375c7ee44 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -137,7 +137,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, switch (type) { case RCAR_DU_ENCODER_LVDS: return rcar_du_lvds_connector_init(rcdu, renc, - &data->u.lvds.panel); + &data->connector.lvds.panel); case RCAR_DU_ENCODER_VGA: return rcar_du_vga_connector_init(rcdu, renc); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index 4f76e16bca88..08cde1293892 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -14,10 +14,11 @@ #ifndef __RCAR_DU_ENCODER_H__ #define __RCAR_DU_ENCODER_H__ +#include + #include struct rcar_du_device; -struct rcar_du_encoder_data; struct rcar_du_encoder { struct drm_encoder encoder; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 3f8483cc0483..a8eef167d51a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -191,7 +191,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) const struct rcar_du_encoder_data *pdata = &rcdu->pdata->encoders[i]; - if (pdata->encoder == RCAR_DU_ENCODER_UNUSED) + if (pdata->type == RCAR_DU_ENCODER_UNUSED) continue; if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) { @@ -201,8 +201,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) continue; } - rcar_du_encoder_init(rcdu, pdata->encoder, pdata->output, - pdata); + rcar_du_encoder_init(rcdu, pdata->type, pdata->output, pdata); } /* Set the possible CRTCs and possible clones. All encoders can be diff --git a/include/linux/platform_data/rcar-du.h b/include/linux/platform_data/rcar-du.h index 80587fdbba3e..64cd8635e6e6 100644 --- a/include/linux/platform_data/rcar-du.h +++ b/include/linux/platform_data/rcar-du.h @@ -28,22 +28,29 @@ struct rcar_du_panel_data { struct drm_mode_modeinfo mode; }; -struct rcar_du_encoder_lvds_data { +struct rcar_du_connector_lvds_data { struct rcar_du_panel_data panel; }; -struct rcar_du_encoder_vga_data { +struct rcar_du_connector_vga_data { /* TODO: Add DDC information for EDID retrieval */ }; +/* + * struct rcar_du_encoder_data - Encoder platform data + * @type: the encoder type (RCAR_DU_ENCODER_*) + * @output: the DU output the connector is connected to + * @connector.lvds: platform data for LVDS connectors + * @connector.vga: platform data for VGA connectors + */ struct rcar_du_encoder_data { - enum rcar_du_encoder_type encoder; + enum rcar_du_encoder_type type; unsigned int output; union { - struct rcar_du_encoder_lvds_data lvds; - struct rcar_du_encoder_vga_data vga; - } u; + struct rcar_du_connector_lvds_data lvds; + struct rcar_du_connector_vga_data vga; + } connector; }; struct rcar_du_platform_data { -- cgit v1.2.3-71-gd317 From ef67a902e946ad1ef51040cf287a45cc4714e2b5 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 17 Jun 2013 03:13:11 +0200 Subject: drm/rcar-du: Rework output routing support Split the output routing specification between SoC-internal data, specified in the rcar_du_device_info structure, and board data, passed through platform data. The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). SoC-internal output routing data specify which output are valid, which CRTCs can be connected to the valid outputs, and the type of in-SoC encoder for the output. Platform data then specifies external encoders and the output they are connected to. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 6 ++++-- drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 4 +++- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 30 ++++++++++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_drv.h | 16 ++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 26 +++++++++++++++++++++----- drivers/gpu/drm/rcar-du/rcar_du_encoder.h | 5 +++-- drivers/gpu/drm/rcar-du/rcar_du_group.c | 8 ++++---- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 17 +++++++++++------ include/linux/platform_data/rcar-du.h | 17 +++++++++++++++-- 9 files changed, 107 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index a340224e08e6..680606ef11d8 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -129,14 +129,16 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay); } -void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output) +void rcar_du_crtc_route_output(struct drm_crtc *crtc, + enum rcar_du_output output) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct rcar_du_device *rcdu = rcrtc->group->dev; /* Store the route from the CRTC output to the DU output. The DU will be * configured when starting the CRTC. */ - rcrtc->outputs |= 1 << output; + rcrtc->outputs |= BIT(output); } void rcar_du_crtc_update_planes(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 542a7feceb20..39a983d13afb 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -15,6 +15,7 @@ #define __RCAR_DU_CRTC_H__ #include +#include #include #include @@ -45,7 +46,8 @@ void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc); -void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output); +void rcar_du_crtc_route_output(struct drm_crtc *crtc, + enum rcar_du_output output); void rcar_du_crtc_update_planes(struct drm_crtc *crtc); #endif /* __RCAR_DU_CRTC_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index f8785357b599..4bc399734490 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -219,12 +219,42 @@ static int rcar_du_remove(struct platform_device *pdev) static const struct rcar_du_device_info rcar_du_r8a7779_info = { .features = 0, .num_crtcs = 2, + .routes = { + /* R8A7779 has two RGB outputs and one (currently unsupported) + * TCON output. + */ + [RCAR_DU_OUTPUT_DPAD0] = { + .possible_crtcs = BIT(0), + .encoder_type = DRM_MODE_ENCODER_NONE, + }, + [RCAR_DU_OUTPUT_DPAD1] = { + .possible_crtcs = BIT(1) | BIT(0), + .encoder_type = DRM_MODE_ENCODER_NONE, + }, + }, }; static const struct rcar_du_device_info rcar_du_r8a7790_info = { .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_ALIGN_128B | RCAR_DU_FEATURE_DEFR8, .num_crtcs = 3, + .routes = { + /* R8A7790 has one RGB output, two LVDS outputs and one + * (currently unsupported) TCON output. + */ + [RCAR_DU_OUTPUT_DPAD0] = { + .possible_crtcs = BIT(2) | BIT(1) | BIT(0), + .encoder_type = DRM_MODE_ENCODER_NONE, + }, + [RCAR_DU_OUTPUT_LVDS0] = { + .possible_crtcs = BIT(0), + .encoder_type = DRM_MODE_ENCODER_LVDS, + }, + [RCAR_DU_OUTPUT_LVDS1] = { + .possible_crtcs = BIT(2) | BIT(1), + .encoder_type = DRM_MODE_ENCODER_LVDS, + }, + }, }; static const struct platform_device_id rcar_du_id_table[] = { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 70c335f51136..d5243f493903 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -29,14 +29,30 @@ struct rcar_du_device; #define RCAR_DU_FEATURE_ALIGN_128B (1 << 1) /* Align pitches to 128 bytes */ #define RCAR_DU_FEATURE_DEFR8 (1 << 2) /* Has DEFR8 register */ +/* + * struct rcar_du_output_routing - Output routing specification + * @possible_crtcs: bitmask of possible CRTCs for the output + * @encoder_type: DRM type of the internal encoder associated with the output + * + * The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data + * specify the valid SoC outputs, which CRTCs can drive the output, and the type + * of in-SoC encoder for the output. + */ +struct rcar_du_output_routing { + unsigned int possible_crtcs; + unsigned int encoder_type; +}; + /* * struct rcar_du_device_info - DU model-specific information * @features: device features (RCAR_DU_FEATURE_*) * @num_crtcs: total number of CRTCs + * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*) */ struct rcar_du_device_info { unsigned int features; unsigned int num_crtcs; + struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX]; }; struct rcar_du_device { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 0d0375c7ee44..2aac28d21f87 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -115,10 +115,12 @@ static const struct drm_encoder_funcs encoder_funcs = { }; int rcar_du_encoder_init(struct rcar_du_device *rcdu, - enum rcar_du_encoder_type type, unsigned int output, + enum rcar_du_encoder_type type, + enum rcar_du_output output, const struct rcar_du_encoder_data *data) { struct rcar_du_encoder *renc; + unsigned int encoder_type; int ret; renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); @@ -127,19 +129,33 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, renc->output = output; + switch (type) { + case RCAR_DU_ENCODER_VGA: + encoder_type = DRM_MODE_ENCODER_DAC; + break; + case RCAR_DU_ENCODER_LVDS: + encoder_type = DRM_MODE_ENCODER_LVDS; + break; + case RCAR_DU_ENCODER_NONE: + default: + /* No external encoder, use the internal encoder type. */ + encoder_type = rcdu->info->routes[output].encoder_type; + break; + } + ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs, - type); + encoder_type); if (ret < 0) return ret; drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs); - switch (type) { - case RCAR_DU_ENCODER_LVDS: + switch (encoder_type) { + case DRM_MODE_ENCODER_LVDS: return rcar_du_lvds_connector_init(rcdu, renc, &data->connector.lvds.panel); - case RCAR_DU_ENCODER_VGA: + case DRM_MODE_ENCODER_DAC: return rcar_du_vga_connector_init(rcdu, renc); default: diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index 08cde1293892..2310416ea21f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -22,7 +22,7 @@ struct rcar_du_device; struct rcar_du_encoder { struct drm_encoder encoder; - unsigned int output; + enum rcar_du_output output; }; #define to_rcar_encoder(e) \ @@ -40,7 +40,8 @@ struct drm_encoder * rcar_du_connector_best_encoder(struct drm_connector *connector); int rcar_du_encoder_init(struct rcar_du_device *rcdu, - enum rcar_du_encoder_type type, unsigned int output, + enum rcar_du_encoder_type type, + enum rcar_du_output output, const struct rcar_du_encoder_data *data); #endif /* __RCAR_DU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c index f3ba0ca845e2..9df6fb635c96 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c @@ -135,11 +135,11 @@ void rcar_du_group_set_routing(struct rcar_du_group *rgrp) dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK); - /* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and - * CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by - * default. + /* Set the DPAD1 pins sources. Select CRTC 0 if explicitly requested and + * CRTC 1 in all other cases to avoid cloning CRTC 0 to DPAD0 and DPAD1 + * by default. */ - if (crtc0->outputs & (1 << 1)) + if (crtc0->outputs & BIT(RCAR_DU_OUTPUT_DPAD1)) dorcr |= DORCR_PG2D_DS1; else dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 816963ca1626..2b92e68a09f0 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -220,11 +220,14 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) for (i = 0; i < rcdu->pdata->num_encoders; ++i) { const struct rcar_du_encoder_data *pdata = &rcdu->pdata->encoders[i]; + const struct rcar_du_output_routing *route = + &rcdu->info->routes[pdata->output]; if (pdata->type == RCAR_DU_ENCODER_UNUSED) continue; - if (pdata->output >= rcdu->num_crtcs) { + if (pdata->output >= RCAR_DU_OUTPUT_MAX || + route->possible_crtcs == 0) { dev_warn(rcdu->dev, "encoder %u references unexisting output %u, skipping\n", i, pdata->output); @@ -234,15 +237,17 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) rcar_du_encoder_init(rcdu, pdata->type, pdata->output, pdata); } - /* Set the possible CRTCs and possible clones. All encoders can be - * driven by the CRTC associated with the output they're connected to, - * as well as by CRTC 0. + /* Set the possible CRTCs and possible clones. There's always at least + * one way for all encoders to clone each other, set all bits in the + * possible clones field. */ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + const struct rcar_du_output_routing *route = + &rcdu->info->routes[renc->output]; - encoder->possible_crtcs = (1 << 0) | (1 << renc->output); - encoder->possible_clones = 1 << 0; + encoder->possible_crtcs = route->possible_crtcs; + encoder->possible_clones = (1 << rcdu->pdata->num_encoders) - 1; } /* Now that the CRTCs have been initialized register the planes. */ diff --git a/include/linux/platform_data/rcar-du.h b/include/linux/platform_data/rcar-du.h index 64cd8635e6e6..1a2e9901a22e 100644 --- a/include/linux/platform_data/rcar-du.h +++ b/include/linux/platform_data/rcar-du.h @@ -16,8 +16,18 @@ #include +enum rcar_du_output { + RCAR_DU_OUTPUT_DPAD0, + RCAR_DU_OUTPUT_DPAD1, + RCAR_DU_OUTPUT_LVDS0, + RCAR_DU_OUTPUT_LVDS1, + RCAR_DU_OUTPUT_TCON, + RCAR_DU_OUTPUT_MAX, +}; + enum rcar_du_encoder_type { RCAR_DU_ENCODER_UNUSED = 0, + RCAR_DU_ENCODER_NONE, RCAR_DU_ENCODER_VGA, RCAR_DU_ENCODER_LVDS, }; @@ -39,13 +49,16 @@ struct rcar_du_connector_vga_data { /* * struct rcar_du_encoder_data - Encoder platform data * @type: the encoder type (RCAR_DU_ENCODER_*) - * @output: the DU output the connector is connected to + * @output: the DU output the connector is connected to (RCAR_DU_OUTPUT_*) * @connector.lvds: platform data for LVDS connectors * @connector.vga: platform data for VGA connectors + * + * Encoder platform data describes an on-board encoder, its associated DU SoC + * output, and the connector. */ struct rcar_du_encoder_data { enum rcar_du_encoder_type type; - unsigned int output; + enum rcar_du_output output; union { struct rcar_du_connector_lvds_data lvds; -- cgit v1.2.3-71-gd317 From 64e8674fbe6bc848333a9b7e19f8cc019dde9eab Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 8 Aug 2013 14:09:24 -0600 Subject: PCI: Add pci_reset_bridge_secondary_bus() Move the secondary bus reset code from pci_parent_bus_reset() into its own function. Export it as we'll later be calling it from hotplug controllers and elsewhere. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 32 +++++++++++++++++++++++--------- include/linux/pci.h | 1 + 2 files changed, 24 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e37fea6e178d..d46860898e1f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3215,9 +3215,30 @@ static int pci_pm_reset(struct pci_dev *dev, int probe) return 0; } -static int pci_parent_bus_reset(struct pci_dev *dev, int probe) +/** + * pci_reset_bridge_secondary_bus - Reset the secondary bus on a PCI bridge. + * @dev: Bridge device + * + * Use the bridge control register to assert reset on the secondary bus. + * Devices on the secondary bus are left in power-on state. + */ +void pci_reset_bridge_secondary_bus(struct pci_dev *dev) { u16 ctrl; + + pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl); + ctrl |= PCI_BRIDGE_CTL_BUS_RESET; + pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl); + msleep(100); + + ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; + pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl); + msleep(100); +} +EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus); + +static int pci_parent_bus_reset(struct pci_dev *dev, int probe) +{ struct pci_dev *pdev; if (pci_is_root_bus(dev->bus) || dev->subordinate || !dev->bus->self) @@ -3230,14 +3251,7 @@ static int pci_parent_bus_reset(struct pci_dev *dev, int probe) if (probe) return 0; - pci_read_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, &ctrl); - ctrl |= PCI_BRIDGE_CTL_BUS_RESET; - pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl); - msleep(100); - - ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; - pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl); - msleep(100); + pci_reset_bridge_secondary_bus(dev->bus->self); return 0; } diff --git a/include/linux/pci.h b/include/linux/pci.h index 0fd1f1582fa1..35c1bc4909f3 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -924,6 +924,7 @@ int pcie_set_mps(struct pci_dev *dev, int mps); int __pci_reset_function(struct pci_dev *dev); int __pci_reset_function_locked(struct pci_dev *dev); int pci_reset_function(struct pci_dev *dev); +void pci_reset_bridge_secondary_bus(struct pci_dev *dev); void pci_update_resource(struct pci_dev *dev, int resno); int __must_check pci_assign_resource(struct pci_dev *dev, int i); int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align); -- cgit v1.2.3-71-gd317 From c88a1f8b96e7384627b918dfabbfc0c615a4a914 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Tue, 6 Aug 2013 22:53:08 +0530 Subject: cpufreq: Store cpufreq policies in a list Policies available in the cpufreq framework are now linked together. They are accessible via cpufreq_policy_list defined in the cpufreq core. [rjw: Fix from Yinghai Lu folded in] Signed-off-by: Lukasz Majewski Signed-off-by: Myungjoo Ham Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 12 ++++++++++++ include/linux/cpufreq.h | 1 + 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 9e83d9142072..75c5bd424d59 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -40,6 +40,7 @@ static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback); static DEFINE_RWLOCK(cpufreq_driver_lock); static DEFINE_MUTEX(cpufreq_governor_lock); +static LIST_HEAD(cpufreq_policy_list); #ifdef CONFIG_HOTPLUG_CPU /* This one keeps track of the previously set governor of a removed CPU */ @@ -952,6 +953,7 @@ static struct cpufreq_policy *cpufreq_policy_alloc(void) if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL)) goto err_free_cpumask; + INIT_LIST_HEAD(&policy->policy_list); return policy; err_free_cpumask: @@ -964,6 +966,12 @@ err_free_policy: static void cpufreq_policy_free(struct cpufreq_policy *policy) { + unsigned long flags; + + write_lock_irqsave(&cpufreq_driver_lock, flags); + list_del(&policy->policy_list); + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + free_cpumask_var(policy->related_cpus); free_cpumask_var(policy->cpus); kfree(policy); @@ -1077,6 +1085,10 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, ret = cpufreq_add_dev_interface(policy, dev); if (ret) goto err_out_unregister; + + write_lock_irqsave(&cpufreq_driver_lock, flags); + list_add(&policy->policy_list, &cpufreq_policy_list); + write_unlock_irqrestore(&cpufreq_driver_lock, flags); } cpufreq_init_policy(policy); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 29208923c9d3..431a05d4b4c9 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -82,6 +82,7 @@ struct cpufreq_policy { struct cpufreq_real_policy user_policy; + struct list_head policy_list; struct kobject kobj; struct completion kobj_unregister; int transition_ongoing; /* Tracks transition status */ -- cgit v1.2.3-71-gd317 From adc97d6a735dbb1e94cb4f1bf0b55f258b349941 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 6 Aug 2013 22:53:12 +0530 Subject: cpufreq: Drop the owner field from struct cpufreq_driver We don't need to set .owner = THIS_MODULE any more in cpufreq drivers as this field isn't used any more by the cpufreq core. This patch removes it and updates all dependent drivers accordingly. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- Documentation/cpu-freq/cpu-drivers.txt | 2 -- drivers/cpufreq/acpi-cpufreq.c | 1 - drivers/cpufreq/at32ap-cpufreq.c | 1 - drivers/cpufreq/blackfin-cpufreq.c | 1 - drivers/cpufreq/cpufreq-nforce2.c | 1 - drivers/cpufreq/cris-artpec3-cpufreq.c | 1 - drivers/cpufreq/cris-etraxfs-cpufreq.c | 1 - drivers/cpufreq/e_powersaver.c | 1 - drivers/cpufreq/elanfreq.c | 1 - drivers/cpufreq/gx-suspmod.c | 1 - drivers/cpufreq/ia64-acpi-cpufreq.c | 1 - drivers/cpufreq/intel_pstate.c | 1 - drivers/cpufreq/kirkwood-cpufreq.c | 1 - drivers/cpufreq/longhaul.c | 1 - drivers/cpufreq/longrun.c | 1 - drivers/cpufreq/loongson2_cpufreq.c | 1 - drivers/cpufreq/maple-cpufreq.c | 1 - drivers/cpufreq/p4-clockmod.c | 1 - drivers/cpufreq/pasemi-cpufreq.c | 1 - drivers/cpufreq/pcc-cpufreq.c | 1 - drivers/cpufreq/pmac32-cpufreq.c | 1 - drivers/cpufreq/pmac64-cpufreq.c | 1 - drivers/cpufreq/powernow-k6.c | 1 - drivers/cpufreq/powernow-k7.c | 1 - drivers/cpufreq/powernow-k8.c | 1 - drivers/cpufreq/ppc-corenet-cpufreq.c | 1 - drivers/cpufreq/ppc_cbe_cpufreq.c | 1 - drivers/cpufreq/s3c2416-cpufreq.c | 1 - drivers/cpufreq/s3c64xx-cpufreq.c | 1 - drivers/cpufreq/sc520_freq.c | 1 - drivers/cpufreq/sh-cpufreq.c | 1 - drivers/cpufreq/sparc-us2e-cpufreq.c | 1 - drivers/cpufreq/sparc-us3-cpufreq.c | 1 - drivers/cpufreq/speedstep-centrino.c | 1 - drivers/cpufreq/speedstep-ich.c | 1 - drivers/cpufreq/speedstep-smi.c | 1 - include/linux/cpufreq.h | 1 - 37 files changed, 38 deletions(-) (limited to 'include') diff --git a/Documentation/cpu-freq/cpu-drivers.txt b/Documentation/cpu-freq/cpu-drivers.txt index 19fa98e07bf7..40282e617913 100644 --- a/Documentation/cpu-freq/cpu-drivers.txt +++ b/Documentation/cpu-freq/cpu-drivers.txt @@ -50,8 +50,6 @@ What shall this struct cpufreq_driver contain? cpufreq_driver.name - The name of this driver. -cpufreq_driver.owner - THIS_MODULE; - cpufreq_driver.init - A pointer to the per-CPU initialization function. diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 44758ce9936d..9b5d1b1a9c03 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -936,7 +936,6 @@ static struct cpufreq_driver acpi_cpufreq_driver = { .exit = acpi_cpufreq_cpu_exit, .resume = acpi_cpufreq_resume, .name = "acpi-cpufreq", - .owner = THIS_MODULE, .attr = acpi_cpufreq_attr, }; diff --git a/drivers/cpufreq/at32ap-cpufreq.c b/drivers/cpufreq/at32ap-cpufreq.c index 654488723cb5..e0c38d938997 100644 --- a/drivers/cpufreq/at32ap-cpufreq.c +++ b/drivers/cpufreq/at32ap-cpufreq.c @@ -108,7 +108,6 @@ static int __init at32_cpufreq_driver_init(struct cpufreq_policy *policy) static struct cpufreq_driver at32_driver = { .name = "at32ap", - .owner = THIS_MODULE, .init = at32_cpufreq_driver_init, .verify = at32_verify_speed, .target = at32_set_target, diff --git a/drivers/cpufreq/blackfin-cpufreq.c b/drivers/cpufreq/blackfin-cpufreq.c index 9cdbbd278a80..ef05978a7237 100644 --- a/drivers/cpufreq/blackfin-cpufreq.c +++ b/drivers/cpufreq/blackfin-cpufreq.c @@ -225,7 +225,6 @@ static struct cpufreq_driver bfin_driver = { .get = bfin_getfreq_khz, .init = __bfin_cpu_init, .name = "bfin cpufreq", - .owner = THIS_MODULE, .attr = bfin_freq_attr, }; diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c index af1542d41440..b83d45f68574 100644 --- a/drivers/cpufreq/cpufreq-nforce2.c +++ b/drivers/cpufreq/cpufreq-nforce2.c @@ -379,7 +379,6 @@ static struct cpufreq_driver nforce2_driver = { .get = nforce2_get, .init = nforce2_cpu_init, .exit = nforce2_cpu_exit, - .owner = THIS_MODULE, }; #ifdef MODULE diff --git a/drivers/cpufreq/cris-artpec3-cpufreq.c b/drivers/cpufreq/cris-artpec3-cpufreq.c index ee142c490575..cb8276dd19ca 100644 --- a/drivers/cpufreq/cris-artpec3-cpufreq.c +++ b/drivers/cpufreq/cris-artpec3-cpufreq.c @@ -111,7 +111,6 @@ static struct cpufreq_driver cris_freq_driver = { .init = cris_freq_cpu_init, .exit = cris_freq_cpu_exit, .name = "cris_freq", - .owner = THIS_MODULE, .attr = cris_freq_attr, }; diff --git a/drivers/cpufreq/cris-etraxfs-cpufreq.c b/drivers/cpufreq/cris-etraxfs-cpufreq.c index 12952235d5db..72328f77dc53 100644 --- a/drivers/cpufreq/cris-etraxfs-cpufreq.c +++ b/drivers/cpufreq/cris-etraxfs-cpufreq.c @@ -108,7 +108,6 @@ static struct cpufreq_driver cris_freq_driver = { .init = cris_freq_cpu_init, .exit = cris_freq_cpu_exit, .name = "cris_freq", - .owner = THIS_MODULE, .attr = cris_freq_attr, }; diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c index de974be6b773..09f64cc83019 100644 --- a/drivers/cpufreq/e_powersaver.c +++ b/drivers/cpufreq/e_powersaver.c @@ -436,7 +436,6 @@ static struct cpufreq_driver eps_driver = { .exit = eps_cpu_exit, .get = eps_get, .name = "e_powersaver", - .owner = THIS_MODULE, .attr = eps_attr, }; diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c index 658d860344b0..823a400d98fd 100644 --- a/drivers/cpufreq/elanfreq.c +++ b/drivers/cpufreq/elanfreq.c @@ -274,7 +274,6 @@ static struct cpufreq_driver elanfreq_driver = { .init = elanfreq_cpu_init, .exit = elanfreq_cpu_exit, .name = "elanfreq", - .owner = THIS_MODULE, .attr = elanfreq_attr, }; diff --git a/drivers/cpufreq/gx-suspmod.c b/drivers/cpufreq/gx-suspmod.c index 4f25fb63c097..ef5fee7dc0b3 100644 --- a/drivers/cpufreq/gx-suspmod.c +++ b/drivers/cpufreq/gx-suspmod.c @@ -446,7 +446,6 @@ static struct cpufreq_driver gx_suspmod_driver = { .target = cpufreq_gx_target, .init = cpufreq_gx_cpu_init, .name = "gx-suspmod", - .owner = THIS_MODULE, }; static int __init cpufreq_gx_init(void) diff --git a/drivers/cpufreq/ia64-acpi-cpufreq.c b/drivers/cpufreq/ia64-acpi-cpufreq.c index 08792dd4d62a..3e14f0317175 100644 --- a/drivers/cpufreq/ia64-acpi-cpufreq.c +++ b/drivers/cpufreq/ia64-acpi-cpufreq.c @@ -409,7 +409,6 @@ static struct cpufreq_driver acpi_cpufreq_driver = { .init = acpi_cpufreq_cpu_init, .exit = acpi_cpufreq_cpu_exit, .name = "acpi-cpufreq", - .owner = THIS_MODULE, .attr = acpi_cpufreq_attr, }; diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 7cde885011ed..6efd96c196b2 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -665,7 +665,6 @@ static struct cpufreq_driver intel_pstate_driver = { .init = intel_pstate_cpu_init, .exit = intel_pstate_cpu_exit, .name = "intel_pstate", - .owner = THIS_MODULE, }; static int __initdata no_load; diff --git a/drivers/cpufreq/kirkwood-cpufreq.c b/drivers/cpufreq/kirkwood-cpufreq.c index c233ea617366..45e4d7fc261d 100644 --- a/drivers/cpufreq/kirkwood-cpufreq.c +++ b/drivers/cpufreq/kirkwood-cpufreq.c @@ -158,7 +158,6 @@ static struct cpufreq_driver kirkwood_cpufreq_driver = { .init = kirkwood_cpufreq_cpu_init, .exit = kirkwood_cpufreq_cpu_exit, .name = "kirkwood-cpufreq", - .owner = THIS_MODULE, .attr = kirkwood_cpufreq_attr, }; diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c index 8c49261df57d..4ada1cccb052 100644 --- a/drivers/cpufreq/longhaul.c +++ b/drivers/cpufreq/longhaul.c @@ -948,7 +948,6 @@ static struct cpufreq_driver longhaul_driver = { .init = longhaul_cpu_init, .exit = longhaul_cpu_exit, .name = "longhaul", - .owner = THIS_MODULE, .attr = longhaul_attr, }; diff --git a/drivers/cpufreq/longrun.c b/drivers/cpufreq/longrun.c index 0fe041d1f77f..5aa031612d53 100644 --- a/drivers/cpufreq/longrun.c +++ b/drivers/cpufreq/longrun.c @@ -286,7 +286,6 @@ static struct cpufreq_driver longrun_driver = { .get = longrun_get, .init = longrun_cpu_init, .name = "longrun", - .owner = THIS_MODULE, }; static const struct x86_cpu_id longrun_ids[] = { diff --git a/drivers/cpufreq/loongson2_cpufreq.c b/drivers/cpufreq/loongson2_cpufreq.c index bb838b985077..e65de91b534b 100644 --- a/drivers/cpufreq/loongson2_cpufreq.c +++ b/drivers/cpufreq/loongson2_cpufreq.c @@ -157,7 +157,6 @@ static struct freq_attr *loongson2_table_attr[] = { }; static struct cpufreq_driver loongson2_cpufreq_driver = { - .owner = THIS_MODULE, .name = "loongson2", .init = loongson2_cpufreq_cpu_init, .verify = loongson2_cpufreq_verify, diff --git a/drivers/cpufreq/maple-cpufreq.c b/drivers/cpufreq/maple-cpufreq.c index cdd62915efaf..41c601f4631e 100644 --- a/drivers/cpufreq/maple-cpufreq.c +++ b/drivers/cpufreq/maple-cpufreq.c @@ -190,7 +190,6 @@ static int maple_cpufreq_cpu_init(struct cpufreq_policy *policy) static struct cpufreq_driver maple_cpufreq_driver = { .name = "maple", - .owner = THIS_MODULE, .flags = CPUFREQ_CONST_LOOPS, .init = maple_cpufreq_cpu_init, .verify = maple_cpufreq_verify, diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c index 9ee78170ff86..2f0a2a65c37f 100644 --- a/drivers/cpufreq/p4-clockmod.c +++ b/drivers/cpufreq/p4-clockmod.c @@ -279,7 +279,6 @@ static struct cpufreq_driver p4clockmod_driver = { .exit = cpufreq_p4_cpu_exit, .get = cpufreq_p4_get, .name = "p4-clockmod", - .owner = THIS_MODULE, .attr = p4clockmod_attr, }; diff --git a/drivers/cpufreq/pasemi-cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c index b704da404067..534e43a60d1f 100644 --- a/drivers/cpufreq/pasemi-cpufreq.c +++ b/drivers/cpufreq/pasemi-cpufreq.c @@ -297,7 +297,6 @@ static int pas_cpufreq_target(struct cpufreq_policy *policy, static struct cpufreq_driver pas_cpufreq_driver = { .name = "pas-cpufreq", - .owner = THIS_MODULE, .flags = CPUFREQ_CONST_LOOPS, .init = pas_cpufreq_cpu_init, .exit = pas_cpufreq_cpu_exit, diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c index 1581fcc4cf4a..d81c4e5ea0ad 100644 --- a/drivers/cpufreq/pcc-cpufreq.c +++ b/drivers/cpufreq/pcc-cpufreq.c @@ -587,7 +587,6 @@ static struct cpufreq_driver pcc_cpufreq_driver = { .init = pcc_cpufreq_cpu_init, .exit = pcc_cpufreq_cpu_exit, .name = "pcc-cpufreq", - .owner = THIS_MODULE, }; static int __init pcc_cpufreq_init(void) diff --git a/drivers/cpufreq/pmac32-cpufreq.c b/drivers/cpufreq/pmac32-cpufreq.c index 3104fad82480..38cdc63c38da 100644 --- a/drivers/cpufreq/pmac32-cpufreq.c +++ b/drivers/cpufreq/pmac32-cpufreq.c @@ -477,7 +477,6 @@ static struct cpufreq_driver pmac_cpufreq_driver = { .flags = CPUFREQ_PM_NO_WARN, .attr = pmac_cpu_freqs_attr, .name = "powermac", - .owner = THIS_MODULE, }; diff --git a/drivers/cpufreq/pmac64-cpufreq.c b/drivers/cpufreq/pmac64-cpufreq.c index 4d7799b7af86..b6850d97f0d5 100644 --- a/drivers/cpufreq/pmac64-cpufreq.c +++ b/drivers/cpufreq/pmac64-cpufreq.c @@ -371,7 +371,6 @@ static int g5_cpufreq_cpu_init(struct cpufreq_policy *policy) static struct cpufreq_driver g5_cpufreq_driver = { .name = "powermac", - .owner = THIS_MODULE, .flags = CPUFREQ_CONST_LOOPS, .init = g5_cpufreq_cpu_init, .verify = g5_cpufreq_verify, diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c index ea8e10382ec5..85f1c8c25ddc 100644 --- a/drivers/cpufreq/powernow-k6.c +++ b/drivers/cpufreq/powernow-k6.c @@ -207,7 +207,6 @@ static struct cpufreq_driver powernow_k6_driver = { .exit = powernow_k6_cpu_exit, .get = powernow_k6_get, .name = "powernow-k6", - .owner = THIS_MODULE, .attr = powernow_k6_attr, }; diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c index 4687d40d0219..14ce480be8ab 100644 --- a/drivers/cpufreq/powernow-k7.c +++ b/drivers/cpufreq/powernow-k7.c @@ -716,7 +716,6 @@ static struct cpufreq_driver powernow_driver = { .init = powernow_cpu_init, .exit = powernow_cpu_exit, .name = "powernow-k7", - .owner = THIS_MODULE, .attr = powernow_table_attr, }; diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index 3f3c6cae91d0..2344a9ed17f3 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -1240,7 +1240,6 @@ static struct cpufreq_driver cpufreq_amd64_driver = { .exit = powernowk8_cpu_exit, .get = powernowk8_get, .name = "powernow-k8", - .owner = THIS_MODULE, .attr = powernow_k8_attr, }; diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c index 3cae4529f959..60e81d524ea8 100644 --- a/drivers/cpufreq/ppc-corenet-cpufreq.c +++ b/drivers/cpufreq/ppc-corenet-cpufreq.c @@ -300,7 +300,6 @@ static struct freq_attr *corenet_cpufreq_attr[] = { static struct cpufreq_driver ppc_corenet_cpufreq_driver = { .name = "ppc_cpufreq", - .owner = THIS_MODULE, .flags = CPUFREQ_CONST_LOOPS, .init = corenet_cpufreq_cpu_init, .exit = __exit_p(corenet_cpufreq_cpu_exit), diff --git a/drivers/cpufreq/ppc_cbe_cpufreq.c b/drivers/cpufreq/ppc_cbe_cpufreq.c index 5936f8d6f2cc..2e448f0bbdc5 100644 --- a/drivers/cpufreq/ppc_cbe_cpufreq.c +++ b/drivers/cpufreq/ppc_cbe_cpufreq.c @@ -181,7 +181,6 @@ static struct cpufreq_driver cbe_cpufreq_driver = { .init = cbe_cpufreq_cpu_init, .exit = cbe_cpufreq_cpu_exit, .name = "cbe-cpufreq", - .owner = THIS_MODULE, .flags = CPUFREQ_CONST_LOOPS, }; diff --git a/drivers/cpufreq/s3c2416-cpufreq.c b/drivers/cpufreq/s3c2416-cpufreq.c index ce5b9fca9c18..22dcb81ef9d0 100644 --- a/drivers/cpufreq/s3c2416-cpufreq.c +++ b/drivers/cpufreq/s3c2416-cpufreq.c @@ -524,7 +524,6 @@ static struct freq_attr *s3c2416_cpufreq_attr[] = { }; static struct cpufreq_driver s3c2416_cpufreq_driver = { - .owner = THIS_MODULE, .flags = 0, .verify = s3c2416_cpufreq_verify_speed, .target = s3c2416_cpufreq_set_target, diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c index 13bb4bae64ee..8a72b0c555f8 100644 --- a/drivers/cpufreq/s3c64xx-cpufreq.c +++ b/drivers/cpufreq/s3c64xx-cpufreq.c @@ -263,7 +263,6 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) } static struct cpufreq_driver s3c64xx_cpufreq_driver = { - .owner = THIS_MODULE, .flags = 0, .verify = s3c64xx_cpufreq_verify_speed, .target = s3c64xx_cpufreq_set_target, diff --git a/drivers/cpufreq/sc520_freq.c b/drivers/cpufreq/sc520_freq.c index 77a210975fc4..d6f6c6f4efa7 100644 --- a/drivers/cpufreq/sc520_freq.c +++ b/drivers/cpufreq/sc520_freq.c @@ -147,7 +147,6 @@ static struct cpufreq_driver sc520_freq_driver = { .init = sc520_freq_cpu_init, .exit = sc520_freq_cpu_exit, .name = "sc520_freq", - .owner = THIS_MODULE, .attr = sc520_freq_attr, }; diff --git a/drivers/cpufreq/sh-cpufreq.c b/drivers/cpufreq/sh-cpufreq.c index 73adb64651e8..ffc6d24b0cfb 100644 --- a/drivers/cpufreq/sh-cpufreq.c +++ b/drivers/cpufreq/sh-cpufreq.c @@ -160,7 +160,6 @@ static struct freq_attr *sh_freq_attr[] = { }; static struct cpufreq_driver sh_cpufreq_driver = { - .owner = THIS_MODULE, .name = "sh", .get = sh_cpufreq_get, .target = sh_cpufreq_target, diff --git a/drivers/cpufreq/sparc-us2e-cpufreq.c b/drivers/cpufreq/sparc-us2e-cpufreq.c index 7c43a725e5da..cf5bc2ca16fa 100644 --- a/drivers/cpufreq/sparc-us2e-cpufreq.c +++ b/drivers/cpufreq/sparc-us2e-cpufreq.c @@ -365,7 +365,6 @@ static int __init us2e_freq_init(void) driver->target = us2e_freq_target; driver->get = us2e_freq_get; driver->exit = us2e_freq_cpu_exit; - driver->owner = THIS_MODULE, strcpy(driver->name, "UltraSPARC-IIe"); cpufreq_us2e_driver = driver; diff --git a/drivers/cpufreq/sparc-us3-cpufreq.c b/drivers/cpufreq/sparc-us3-cpufreq.c index 7f500c140bc3..ac76b489979d 100644 --- a/drivers/cpufreq/sparc-us3-cpufreq.c +++ b/drivers/cpufreq/sparc-us3-cpufreq.c @@ -226,7 +226,6 @@ static int __init us3_freq_init(void) driver->target = us3_freq_target; driver->get = us3_freq_get; driver->exit = us3_freq_cpu_exit; - driver->owner = THIS_MODULE, strcpy(driver->name, "UltraSPARC-III"); cpufreq_us3_driver = driver; diff --git a/drivers/cpufreq/speedstep-centrino.c b/drivers/cpufreq/speedstep-centrino.c index 0915e712fbdc..f897d5105842 100644 --- a/drivers/cpufreq/speedstep-centrino.c +++ b/drivers/cpufreq/speedstep-centrino.c @@ -575,7 +575,6 @@ static struct cpufreq_driver centrino_driver = { .target = centrino_target, .get = get_cur_freq, .attr = centrino_attr, - .owner = THIS_MODULE, }; /* diff --git a/drivers/cpufreq/speedstep-ich.c b/drivers/cpufreq/speedstep-ich.c index e2e5aa971452..5355abb69afc 100644 --- a/drivers/cpufreq/speedstep-ich.c +++ b/drivers/cpufreq/speedstep-ich.c @@ -378,7 +378,6 @@ static struct cpufreq_driver speedstep_driver = { .init = speedstep_cpu_init, .exit = speedstep_cpu_exit, .get = speedstep_get, - .owner = THIS_MODULE, .attr = speedstep_attr, }; diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c index f5a6b70ee6c0..abfba4f731eb 100644 --- a/drivers/cpufreq/speedstep-smi.c +++ b/drivers/cpufreq/speedstep-smi.c @@ -375,7 +375,6 @@ static struct cpufreq_driver speedstep_driver = { .exit = speedstep_cpu_exit, .get = speedstep_get, .resume = speedstep_resume, - .owner = THIS_MODULE, .attr = speedstep_attr, }; diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 431a05d4b4c9..d568f3975eeb 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -179,7 +179,6 @@ __ATTR(_name, 0644, show_##_name, store_##_name) struct cpufreq_driver { - struct module *owner; char name[CPUFREQ_NAME_LEN]; u8 flags; /* -- cgit v1.2.3-71-gd317 From 149479d019e06df5a7f4096f95c00cfb1380309c Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Thu, 8 Aug 2013 14:06:22 -0700 Subject: tcp: add server ip to encrypt cookie in fast open Encrypt the cookie with both server and client IPv4 addresses, such that multi-homed server will grant different cookies based on both the source and destination IPs. No client change is needed since cookie is opaque to the client. Signed-off-by: Yuchung Cheng Reviewed-by: Eric Dumazet Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/tcp.h | 3 ++- net/ipv4/tcp_fastopen.c | 13 ++++++------- net/ipv4/tcp_ipv4.c | 10 +++++++--- 3 files changed, 15 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 27b652f379ef..09cb5c11ceea 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1300,7 +1300,8 @@ void tcp_free_fastopen_req(struct tcp_sock *tp); extern struct tcp_fastopen_context __rcu *tcp_fastopen_ctx; int tcp_fastopen_reset_cipher(void *key, unsigned int len); -void tcp_fastopen_cookie_gen(__be32 addr, struct tcp_fastopen_cookie *foc); +extern void tcp_fastopen_cookie_gen(__be32 src, __be32 dst, + struct tcp_fastopen_cookie *foc); #define TCP_FASTOPEN_KEY_LENGTH 16 diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 8f7ef0ad80e5..ab7bd35bb312 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -58,23 +58,22 @@ error: kfree(ctx); return err; } -/* Computes the fastopen cookie for the peer. - * The peer address is a 128 bits long (pad with zeros for IPv4). +/* Computes the fastopen cookie for the IP path. + * The path is a 128 bits long (pad with zeros for IPv4). * * The caller must check foc->len to determine if a valid cookie * has been generated successfully. */ -void tcp_fastopen_cookie_gen(__be32 addr, struct tcp_fastopen_cookie *foc) +void tcp_fastopen_cookie_gen(__be32 src, __be32 dst, + struct tcp_fastopen_cookie *foc) { - __be32 peer_addr[4] = { addr, 0, 0, 0 }; + __be32 path[4] = { src, dst, 0, 0 }; struct tcp_fastopen_context *ctx; rcu_read_lock(); ctx = rcu_dereference(tcp_fastopen_ctx); if (ctx) { - crypto_cipher_encrypt_one(ctx->tfm, - foc->val, - (__u8 *)peer_addr); + crypto_cipher_encrypt_one(ctx->tfm, foc->val, (__u8 *)path); foc->len = TCP_FASTOPEN_COOKIE_SIZE; } rcu_read_unlock(); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 280efe5f19c1..ec2702882d8d 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1316,9 +1316,11 @@ static bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb, tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq; return true; } + if (foc->len == TCP_FASTOPEN_COOKIE_SIZE) { if ((sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_CHKED) == 0) { - tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, valid_foc); + tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, valid_foc); if ((valid_foc->len != TCP_FASTOPEN_COOKIE_SIZE) || memcmp(&foc->val[0], &valid_foc->val[0], TCP_FASTOPEN_COOKIE_SIZE) != 0) @@ -1329,14 +1331,16 @@ static bool tcp_fastopen_check(struct sock *sk, struct sk_buff *skb, tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->end_seq; return true; } else if (foc->len == 0) { /* Client requesting a cookie */ - tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, valid_foc); + tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, valid_foc); NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPFASTOPENCOOKIEREQD); } else { /* Client sent a cookie with wrong size. Treat it * the same as invalid and return a valid one. */ - tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, valid_foc); + tcp_fastopen_cookie_gen(ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, valid_foc); } return false; } -- cgit v1.2.3-71-gd317 From e370a7236321773245c5522d8bb299380830d3b2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 8 Aug 2013 14:37:32 -0700 Subject: af_unix: improve STREAM behavior with fragmented memory unix_stream_sendmsg() currently uses order-2 allocations, and we had numerous reports this can fail. The __GFP_REPEAT flag present in sock_alloc_send_pskb() is not helping. This patch extends the work done in commit eb6a24816b247c ("af_unix: reduce high order page allocations) for datagram sockets. This opens the possibility of zero copy IO (splice() and friends) The trick is to not use skb_pull() anymore in recvmsg() path, and instead add a @consumed field in UNIXCB() to track amount of already read payload in the skb. There is a performance regression for large sends because of extra page allocations that will be addressed in a follow-up patch, allowing sock_alloc_send_pskb() to attempt high order page allocations. Signed-off-by: Eric Dumazet Cc: David Rientjes Signed-off-by: David S. Miller --- include/net/af_unix.h | 1 + net/unix/af_unix.c | 65 ++++++++++++++++++++++++--------------------------- 2 files changed, 31 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/net/af_unix.h b/include/net/af_unix.h index 05442df4ca89..a175ba4a7adb 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -35,6 +35,7 @@ struct unix_skb_parms { #ifdef CONFIG_SECURITY_NETWORK u32 secid; /* Security ID */ #endif + u32 consumed; }; #define UNIXCB(skb) (*(struct unix_skb_parms *)&((skb)->cb)) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index c4ce243824bb..99dc760cdd95 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1596,6 +1596,10 @@ out: return err; } +/* We use paged skbs for stream sockets, and limit occupancy to 32768 + * bytes, and a minimun of a full page. + */ +#define UNIX_SKB_FRAGS_SZ (PAGE_SIZE << get_order(32768)) static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, size_t len) @@ -1609,6 +1613,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, struct scm_cookie tmp_scm; bool fds_sent = false; int max_level; + int data_len; if (NULL == siocb->scm) siocb->scm = &tmp_scm; @@ -1635,40 +1640,21 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, goto pipe_err; while (sent < len) { - /* - * Optimisation for the fact that under 0.01% of X - * messages typically need breaking up. - */ - - size = len-sent; + size = len - sent; /* Keep two messages in the pipe so it schedules better */ - if (size > ((sk->sk_sndbuf >> 1) - 64)) - size = (sk->sk_sndbuf >> 1) - 64; + size = min_t(int, size, (sk->sk_sndbuf >> 1) - 64); - if (size > SKB_MAX_ALLOC) - size = SKB_MAX_ALLOC; - - /* - * Grab a buffer - */ + /* allow fallback to order-0 allocations */ + size = min_t(int, size, SKB_MAX_HEAD(0) + UNIX_SKB_FRAGS_SZ); - skb = sock_alloc_send_skb(sk, size, msg->msg_flags&MSG_DONTWAIT, - &err); + data_len = max_t(int, 0, size - SKB_MAX_HEAD(0)); - if (skb == NULL) + skb = sock_alloc_send_pskb(sk, size - data_len, data_len, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) goto out_err; - /* - * If you pass two values to the sock_alloc_send_skb - * it tries to grab the large buffer with GFP_NOFS - * (which can fail easily), and if it fails grab the - * fallback size buffer which is under a page and will - * succeed. [Alan] - */ - size = min_t(int, size, skb_tailroom(skb)); - - /* Only send the fds in the first buffer */ err = unix_scm_to_skb(siocb->scm, skb, !fds_sent); if (err < 0) { @@ -1678,7 +1664,10 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, max_level = err + 1; fds_sent = true; - err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); + skb_put(skb, size - data_len); + skb->data_len = data_len; + skb->len = size; + err = skb_copy_datagram_from_iovec(skb, 0, msg->msg_iov, 0, size); if (err) { kfree_skb(skb); goto out_err; @@ -1890,6 +1879,11 @@ static long unix_stream_data_wait(struct sock *sk, long timeo, return timeo; } +static unsigned int unix_skb_len(const struct sk_buff *skb) +{ + return skb->len - UNIXCB(skb).consumed; +} + static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags) @@ -1977,8 +1971,8 @@ again: } skip = sk_peek_offset(sk, flags); - while (skip >= skb->len) { - skip -= skb->len; + while (skip >= unix_skb_len(skb)) { + skip -= unix_skb_len(skb); last = skb; skb = skb_peek_next(skb, &sk->sk_receive_queue); if (!skb) @@ -2005,8 +1999,9 @@ again: sunaddr = NULL; } - chunk = min_t(unsigned int, skb->len - skip, size); - if (memcpy_toiovec(msg->msg_iov, skb->data + skip, chunk)) { + chunk = min_t(unsigned int, unix_skb_len(skb) - skip, size); + if (skb_copy_datagram_iovec(skb, UNIXCB(skb).consumed + skip, + msg->msg_iov, chunk)) { if (copied == 0) copied = -EFAULT; break; @@ -2016,14 +2011,14 @@ again: /* Mark read part of skb as used */ if (!(flags & MSG_PEEK)) { - skb_pull(skb, chunk); + UNIXCB(skb).consumed += chunk; sk_peek_offset_bwd(sk, chunk); if (UNIXCB(skb).fp) unix_detach_fds(siocb->scm, skb); - if (skb->len) + if (unix_skb_len(skb)) break; skb_unlink(skb, &sk->sk_receive_queue); @@ -2107,7 +2102,7 @@ long unix_inq_len(struct sock *sk) if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) { skb_queue_walk(&sk->sk_receive_queue, skb) - amount += skb->len; + amount += unix_skb_len(skb); } else { skb = skb_peek(&sk->sk_receive_queue); if (skb) -- cgit v1.2.3-71-gd317 From 28d6427109d13b0f447cba5761f88d3548e83605 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 8 Aug 2013 14:38:47 -0700 Subject: net: attempt high order allocations in sock_alloc_send_pskb() Adding paged frags skbs to af_unix sockets introduced a performance regression on large sends because of additional page allocations, even if each skb could carry at least 100% more payload than before. We can instruct sock_alloc_send_pskb() to attempt high order allocations. Most of the time, it does a single page allocation instead of 8. I added an additional parameter to sock_alloc_send_pskb() to let other users to opt-in for this new feature on followup patches. Tested: Before patch : $ netperf -t STREAM_STREAM STREAM STREAM TEST Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 2304 212992 212992 10.00 46861.15 After patch : $ netperf -t STREAM_STREAM STREAM STREAM TEST Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 2304 212992 212992 10.00 57981.11 Signed-off-by: Eric Dumazet Cc: David Rientjes Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 2 +- drivers/net/tun.c | 2 +- include/net/sock.h | 3 +- net/core/sock.c | 100 +++++++++++++++++++++++++------------------------ net/packet/af_packet.c | 2 +- net/unix/af_unix.c | 6 ++- 6 files changed, 60 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 182364abfa35..8f6056d9ba97 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -524,7 +524,7 @@ static inline struct sk_buff *macvtap_alloc_skb(struct sock *sk, size_t prepad, linear = len; skb = sock_alloc_send_pskb(sk, prepad + linear, len - linear, noblock, - err); + err, 0); if (!skb) return NULL; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index b1630479d4a1..978d8654b14a 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -949,7 +949,7 @@ static struct sk_buff *tun_alloc_skb(struct tun_file *tfile, linear = len; skb = sock_alloc_send_pskb(sk, prepad + linear, len - linear, noblock, - &err); + &err, 0); if (!skb) return ERR_PTR(err); diff --git a/include/net/sock.h b/include/net/sock.h index ab6a8b75207e..e4bbcbfd07ea 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1539,7 +1539,8 @@ extern struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len, unsigned long data_len, int noblock, - int *errcode); + int *errcode, + int max_page_order); extern void *sock_kmalloc(struct sock *sk, int size, gfp_t priority); extern void sock_kfree_s(struct sock *sk, void *mem, int size); diff --git a/net/core/sock.c b/net/core/sock.c index 83667de45ec9..5b6beba494a3 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1741,24 +1741,23 @@ static long sock_wait_for_wmem(struct sock *sk, long timeo) struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len, unsigned long data_len, int noblock, - int *errcode) + int *errcode, int max_page_order) { - struct sk_buff *skb; + struct sk_buff *skb = NULL; + unsigned long chunk; gfp_t gfp_mask; long timeo; int err; int npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + struct page *page; + int i; err = -EMSGSIZE; if (npages > MAX_SKB_FRAGS) goto failure; - gfp_mask = sk->sk_allocation; - if (gfp_mask & __GFP_WAIT) - gfp_mask |= __GFP_REPEAT; - timeo = sock_sndtimeo(sk, noblock); - while (1) { + while (!skb) { err = sock_error(sk); if (err != 0) goto failure; @@ -1767,50 +1766,52 @@ struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len, if (sk->sk_shutdown & SEND_SHUTDOWN) goto failure; - if (atomic_read(&sk->sk_wmem_alloc) < sk->sk_sndbuf) { - skb = alloc_skb(header_len, gfp_mask); - if (skb) { - int i; - - /* No pages, we're done... */ - if (!data_len) - break; - - skb->truesize += data_len; - skb_shinfo(skb)->nr_frags = npages; - for (i = 0; i < npages; i++) { - struct page *page; - - page = alloc_pages(sk->sk_allocation, 0); - if (!page) { - err = -ENOBUFS; - skb_shinfo(skb)->nr_frags = i; - kfree_skb(skb); - goto failure; - } - - __skb_fill_page_desc(skb, i, - page, 0, - (data_len >= PAGE_SIZE ? - PAGE_SIZE : - data_len)); - data_len -= PAGE_SIZE; - } + if (atomic_read(&sk->sk_wmem_alloc) >= sk->sk_sndbuf) { + set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); + set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); + err = -EAGAIN; + if (!timeo) + goto failure; + if (signal_pending(current)) + goto interrupted; + timeo = sock_wait_for_wmem(sk, timeo); + continue; + } - /* Full success... */ - break; - } - err = -ENOBUFS; + err = -ENOBUFS; + gfp_mask = sk->sk_allocation; + if (gfp_mask & __GFP_WAIT) + gfp_mask |= __GFP_REPEAT; + + skb = alloc_skb(header_len, gfp_mask); + if (!skb) goto failure; + + skb->truesize += data_len; + + for (i = 0; npages > 0; i++) { + int order = max_page_order; + + while (order) { + if (npages >= 1 << order) { + page = alloc_pages(sk->sk_allocation | + __GFP_COMP | __GFP_NOWARN, + order); + if (page) + goto fill_page; + } + order--; + } + page = alloc_page(sk->sk_allocation); + if (!page) + goto failure; +fill_page: + chunk = min_t(unsigned long, data_len, + PAGE_SIZE << order); + skb_fill_page_desc(skb, i, page, 0, chunk); + data_len -= chunk; + npages -= 1 << order; } - set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); - set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); - err = -EAGAIN; - if (!timeo) - goto failure; - if (signal_pending(current)) - goto interrupted; - timeo = sock_wait_for_wmem(sk, timeo); } skb_set_owner_w(skb, sk); @@ -1819,6 +1820,7 @@ struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len, interrupted: err = sock_intr_errno(timeo); failure: + kfree_skb(skb); *errcode = err; return NULL; } @@ -1827,7 +1829,7 @@ EXPORT_SYMBOL(sock_alloc_send_pskb); struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, int noblock, int *errcode) { - return sock_alloc_send_pskb(sk, size, 0, noblock, errcode); + return sock_alloc_send_pskb(sk, size, 0, noblock, errcode, 0); } EXPORT_SYMBOL(sock_alloc_send_skb); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 4cb28a7f639b..6c53dd9f5ccc 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2181,7 +2181,7 @@ static struct sk_buff *packet_alloc_skb(struct sock *sk, size_t prepad, linear = len; skb = sock_alloc_send_pskb(sk, prepad + linear, len - linear, noblock, - err); + err, 0); if (!skb) return NULL; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 99dc760cdd95..fee9e3397cd1 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1479,7 +1479,8 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, MAX_SKB_FRAGS * PAGE_SIZE); skb = sock_alloc_send_pskb(sk, len - data_len, data_len, - msg->msg_flags & MSG_DONTWAIT, &err); + msg->msg_flags & MSG_DONTWAIT, &err, + PAGE_ALLOC_COSTLY_ORDER); if (skb == NULL) goto out; @@ -1651,7 +1652,8 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, data_len = max_t(int, 0, size - SKB_MAX_HEAD(0)); skb = sock_alloc_send_pskb(sk, size - data_len, data_len, - msg->msg_flags & MSG_DONTWAIT, &err); + msg->msg_flags & MSG_DONTWAIT, &err, + get_order(UNIX_SKB_FRAGS_SZ)); if (!skb) goto out_err; -- cgit v1.2.3-71-gd317 From c77f872e663e3f6ea18f774bf4399884884b4b22 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 10 Aug 2013 21:33:09 +0200 Subject: ASoC: Remove unused snd_soc_info_volsw_ext() The SOC_SINGLE_EXT control has been using snd_soc_info_volsw() for its info callback since commit 1c433fb ("[ALSA] soc - 0.13 ASoC headers"). The snd_soc_info_volsw_ext() function has been unused ever since then, so remove it. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 2 -- sound/soc/soc-core.c | 26 -------------------------- 2 files changed, 28 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7ec15a..724a42af40fa 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -497,8 +497,6 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); -int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); #define snd_soc_info_bool_ext snd_ctl_boolean_mono_info int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c7d16df9efd9..6ba5f7c23d3a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2577,32 +2577,6 @@ int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext); -/** - * snd_soc_info_volsw_ext - external single mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about a single external mixer control. - * - * Returns 0 for success. - */ -int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - int max = kcontrol->private_value; - - if (max == 1 && !strstr(kcontrol->id.name, " Volume")) - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - else - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = max; - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext); - /** * snd_soc_info_volsw - single mixer info callback * @kcontrol: mixer control -- cgit v1.2.3-71-gd317 From 9a953e6f27fd280a2af5719b77394fbb228c5b46 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 10 Aug 2013 21:33:10 +0200 Subject: ASoC: Use snd_soc_info_enum_double() for SOC_ENUM_EXT controls snd_soc_info_enum_ext() and snd_soc_info_enum_double() are almost identical. The only difference is that snd_soc_info_enum_double() is also able to handle stereo controls. Using snd_soc_info_enum double() instead of snd_soc_info_enum_ext() for the SOC_ENUM_EXT control's info callback allows us to remove snd_soc_info_enum_ext(). Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc.h | 4 +--- sound/soc/soc-core.c | 27 --------------------------- 2 files changed, 1 insertion(+), 30 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 724a42af40fa..6f86a4187f58 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -234,7 +234,7 @@ .private_value = xdata } #define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_soc_info_enum_ext, \ + .info = snd_soc_info_enum_double, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = (unsigned long)&xenum } @@ -485,8 +485,6 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai, const struct snd_kcontrol_new *controls, int num_controls); int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); -int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo); int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6ba5f7c23d3a..f46472d50c9b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2550,33 +2550,6 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double); -/** - * snd_soc_info_enum_ext - external enumerated single mixer info callback - * @kcontrol: mixer control - * @uinfo: control element information - * - * Callback to provide information about an external enumerated - * single mixer. - * - * Returns 0 for success. - */ -int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = e->max; - - if (uinfo->value.enumerated.item > e->max - 1) - uinfo->value.enumerated.item = e->max - 1; - strcpy(uinfo->value.enumerated.name, - e->texts[uinfo->value.enumerated.item]); - return 0; -} -EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext); - /** * snd_soc_info_volsw - single mixer info callback * @kcontrol: mixer control -- cgit v1.2.3-71-gd317 From 77409d9bcc0ee3fc309a3d6fd18553f6ec5a9aa6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 12 Aug 2013 14:21:14 +0530 Subject: regulator: palmas: model SMPS10 as two regulators SMPS10 has two outputs OUT1 and OUT2 and have one input IN1. SMPS10-OUT2 is connected to SMPS10-IN1 and can be configured either in BOOST mode or BYPASS mode. regulator_enable of SMPS10-OUT2 configures it in BOOST mode. For BYPASS mode regulator_allow_bypass() API can be used. SMPS10-OUT1 is connected to SMPS10-OUT2 and can be enabled using regulator_enable(). [ axel.lin@ingics.com : Simplify regulator_desc setting for SMPS10_[OUT1|OUT2] Signed-off-by: J Keerthy Signed-off-by: Laxman Dewangan Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/palmas-regulator.c | 30 ++++++++++++++++++++++++------ include/linux/mfd/palmas.h | 9 +++++---- 2 files changed, 29 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index d0c87856dd25..40ed92ba03f4 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -97,10 +97,15 @@ static const struct regs_info palmas_regs_info[] = { .ctrl_addr = PALMAS_SMPS9_CTRL, }, { - .name = "SMPS10", + .name = "SMPS10_OUT2", .sname = "smps10-in", .ctrl_addr = PALMAS_SMPS10_CTRL, }, + { + .name = "SMPS10_OUT1", + .sname = "smps10-out2", + .ctrl_addr = PALMAS_SMPS10_CTRL, + }, { .name = "LDO1", .sname = "ldo1-in", @@ -487,6 +492,8 @@ static struct regulator_ops palmas_ops_smps10 = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, + .set_bypass = regulator_set_bypass_regmap, + .get_bypass = regulator_get_bypass_regmap, }; static int palmas_is_enabled_ldo(struct regulator_dev *dev) @@ -538,7 +545,8 @@ static int palmas_smps_init(struct palmas *palmas, int id, return ret; switch (id) { - case PALMAS_REG_SMPS10: + case PALMAS_REG_SMPS10_OUT1: + case PALMAS_REG_SMPS10_OUT2: reg &= ~PALMAS_SMPS10_CTRL_MODE_SLEEP_MASK; if (reg_init->mode_sleep) reg |= reg_init->mode_sleep << @@ -681,7 +689,8 @@ static struct of_regulator_match palmas_matches[] = { { .name = "smps7", }, { .name = "smps8", }, { .name = "smps9", }, - { .name = "smps10", }, + { .name = "smps10_out2", }, + { .name = "smps10_out1", }, { .name = "ldo1", }, { .name = "ldo2", }, { .name = "ldo3", }, @@ -838,7 +847,8 @@ static int palmas_regulators_probe(struct platform_device *pdev) continue; ramp_delay_support = true; break; - case PALMAS_REG_SMPS10: + case PALMAS_REG_SMPS10_OUT1: + case PALMAS_REG_SMPS10_OUT2: if (!PALMAS_PMIC_HAS(palmas, SMPS10_BOOST)) continue; } @@ -872,7 +882,8 @@ static int palmas_regulators_probe(struct platform_device *pdev) pmic->desc[id].id = id; switch (id) { - case PALMAS_REG_SMPS10: + case PALMAS_REG_SMPS10_OUT1: + case PALMAS_REG_SMPS10_OUT2: pmic->desc[id].n_voltages = PALMAS_SMPS10_NUM_VOLTAGES; pmic->desc[id].ops = &palmas_ops_smps10; pmic->desc[id].vsel_reg = @@ -882,7 +893,14 @@ static int palmas_regulators_probe(struct platform_device *pdev) pmic->desc[id].enable_reg = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE, PALMAS_SMPS10_CTRL); - pmic->desc[id].enable_mask = SMPS10_BOOST_EN; + if (id == PALMAS_REG_SMPS10_OUT1) + pmic->desc[id].enable_mask = SMPS10_SWITCH_EN; + else + pmic->desc[id].enable_mask = SMPS10_BOOST_EN; + pmic->desc[id].bypass_reg = + PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE, + PALMAS_SMPS10_CTRL); + pmic->desc[id].bypass_mask = SMPS10_BYPASS_EN; pmic->desc[id].min_uV = 3750000; pmic->desc[id].uV_step = 1250000; break; diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 1a8dd7afe084..c06d78af3342 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -160,7 +160,8 @@ enum palmas_regulators { PALMAS_REG_SMPS7, PALMAS_REG_SMPS8, PALMAS_REG_SMPS9, - PALMAS_REG_SMPS10, + PALMAS_REG_SMPS10_OUT2, + PALMAS_REG_SMPS10_OUT1, /* LDO regulators */ PALMAS_REG_LDO1, PALMAS_REG_LDO2, @@ -355,9 +356,9 @@ struct palmas_pmic { int smps123; int smps457; - int range[PALMAS_REG_SMPS10]; - unsigned int ramp_delay[PALMAS_REG_SMPS10]; - unsigned int current_reg_mode[PALMAS_REG_SMPS10]; + int range[PALMAS_REG_SMPS10_OUT1]; + unsigned int ramp_delay[PALMAS_REG_SMPS10_OUT1]; + unsigned int current_reg_mode[PALMAS_REG_SMPS10_OUT1]; }; struct palmas_resource { -- cgit v1.2.3-71-gd317 From af61a165187bb94b1dc7628ef815c23d0eacf40b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 2 Jul 2013 18:09:12 +0200 Subject: mac80211: add control port protocol TX control flag A lot of drivers check the frame protocol for ETH_P_PAE, for various reasons (like making those more reliable). Add a new flags bitmap to the TX control info and a new flag indicating the control port protocol is in use to let all drivers also apply such logic to other control port protocols, should they be configured. Also use the new flag in the iwlwifi drivers. Signed-off-by: Johannes Berg --- Documentation/DocBook/80211.tmpl | 1 + drivers/net/wireless/iwlwifi/dvm/tx.c | 2 +- drivers/net/wireless/iwlwifi/iwl-devtrace.h | 7 ++++--- drivers/net/wireless/iwlwifi/mvm/tx.c | 9 ++++----- include/net/mac80211.h | 19 ++++++++++++++++--- net/mac80211/rc80211_minstrel_ht.c | 5 +++-- net/mac80211/tx.c | 8 +++++--- 7 files changed, 34 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index 49267ea97568..f403ec3c5c9a 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -325,6 +325,7 @@ functions/definitions !Finclude/net/mac80211.h ieee80211_rx_status !Finclude/net/mac80211.h mac80211_rx_flags +!Finclude/net/mac80211.h mac80211_tx_info_flags !Finclude/net/mac80211.h mac80211_tx_control_flags !Finclude/net/mac80211.h mac80211_rate_control_flags !Finclude/net/mac80211.h ieee80211_tx_rate diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 5ee983faa679..f364583b1535 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -87,7 +87,7 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, priv->lib->bt_params->advanced_bt_coexist && (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc) || - skb->protocol == cpu_to_be16(ETH_P_PAE))) + info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) tx_flags |= TX_CMD_FLG_IGNORE_BT; diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index 4491c1c72cc7..684c416d3493 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -33,10 +33,11 @@ static inline bool iwl_trace_data(struct sk_buff *skb) { struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - if (ieee80211_is_data(hdr->frame_control)) - return skb->protocol != cpu_to_be16(ETH_P_PAE); - return false; + if (!ieee80211_is_data(hdr->frame_control)) + return false; + return !(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO); } static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans, diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index f0e96a927407..d62a6d3053ff 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -91,11 +91,10 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR; /* High prio packet (wrt. BT coex) if it is EAPOL, MCAST or MGMT */ - if (info->band == IEEE80211_BAND_2GHZ && - (skb->protocol == cpu_to_be16(ETH_P_PAE) || - is_multicast_ether_addr(hdr->addr1) || - ieee80211_is_back_req(fc) || - ieee80211_is_mgmt(fc))) + if (info->band == IEEE80211_BAND_2GHZ && + (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO || + is_multicast_ether_addr(hdr->addr1) || + ieee80211_is_back_req(fc) || ieee80211_is_mgmt(fc))) tx_flags |= TX_CMD_FLG_BT_DIS; if (ieee80211_has_morefrags(fc)) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9cda3728c2cb..b70c00111323 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -375,7 +375,7 @@ struct ieee80211_bss_conf { }; /** - * enum mac80211_tx_control_flags - flags to describe transmission information/status + * enum mac80211_tx_info_flags - flags to describe transmission information/status * * These flags are used with the @flags member of &ieee80211_tx_info. * @@ -471,7 +471,7 @@ struct ieee80211_bss_conf { * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. */ -enum mac80211_tx_control_flags { +enum mac80211_tx_info_flags { IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0), IEEE80211_TX_CTL_ASSIGN_SEQ = BIT(1), IEEE80211_TX_CTL_NO_ACK = BIT(2), @@ -507,6 +507,18 @@ enum mac80211_tx_control_flags { #define IEEE80211_TX_CTL_STBC_SHIFT 23 +/** + * enum mac80211_tx_control_flags - flags to describe transmit control + * + * @IEEE80211_TX_CTRL_PORT_CTRL_PROTO: this frame is a port control + * protocol frame (e.g. EAP) + * + * These flags are used in tx_info->control.flags. + */ +enum mac80211_tx_control_flags { + IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0), +}; + /* * This definition is used as a mask to clear all temporary flags, which are * set by the tx handlers for each transmission attempt by the mac80211 stack. @@ -680,7 +692,8 @@ struct ieee80211_tx_info { /* NB: vif can be NULL for injected frames */ struct ieee80211_vif *vif; struct ieee80211_key_conf *hw_key; - /* 8 bytes free */ + u32 flags; + /* 4 bytes free */ } control; struct { struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES]; diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 7475a7a33797..9eff3824f2d1 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -439,12 +439,13 @@ minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct sta_info *sta = container_of(pubsta, struct sta_info, sta); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); u16 tid; if (unlikely(!ieee80211_is_data_qos(hdr->frame_control))) return; - if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE))) + if (unlikely(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) return; tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; @@ -776,7 +777,7 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, /* Don't use EAPOL frames for sampling on non-mrr hw */ if (mp->hw->max_rates == 1 && - txrc->skb->protocol == cpu_to_be16(ETH_P_PAE)) + (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) sample_idx = -1; else sample_idx = minstrel_get_sample_rate(mp, mi); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0e42322aa6b1..098ae854ad3c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -539,9 +539,11 @@ ieee80211_tx_h_check_control_port_protocol(struct ieee80211_tx_data *tx) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); - if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol && - tx->sdata->control_port_no_encrypt)) - info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol)) { + if (tx->sdata->control_port_no_encrypt) + info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO; + } return TX_CONTINUE; } -- cgit v1.2.3-71-gd317 From fc73f11f5fa230f8c687d51b0fddb00433092ce0 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Wed, 31 Jul 2013 18:04:15 +0300 Subject: cfg80211: add wdev to testmode cmd To allow drivers to implement per-interface testmode operations more easily, pass a wdev pointer if any identification for one was given from userspace. Clean up the code a bit while at it. Signed-off-by: David Spinadel Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/testmode.c | 3 ++- drivers/net/wireless/ath/ath6kl/testmode.h | 7 +++++-- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 4 +++- include/net/cfg80211.h | 5 +++-- net/mac80211/cfg.c | 4 +++- net/wireless/nl80211.c | 23 ++++++++++++++++------ net/wireless/rdev-ops.h | 5 +++-- net/wireless/trace.h | 8 +++++--- 8 files changed, 41 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath6kl/testmode.c b/drivers/net/wireless/ath/ath6kl/testmode.c index acc9aa832f76..d67170ea1038 100644 --- a/drivers/net/wireless/ath/ath6kl/testmode.c +++ b/drivers/net/wireless/ath/ath6kl/testmode.c @@ -66,7 +66,8 @@ nla_put_failure: ath6kl_warn("nla_put failed on testmode rx skb!\n"); } -int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) +int ath6kl_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, + void *data, int len) { struct ath6kl *ar = wiphy_priv(wiphy); struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1]; diff --git a/drivers/net/wireless/ath/ath6kl/testmode.h b/drivers/net/wireless/ath/ath6kl/testmode.h index fe651d6707df..9fbcdec3e208 100644 --- a/drivers/net/wireless/ath/ath6kl/testmode.h +++ b/drivers/net/wireless/ath/ath6kl/testmode.h @@ -20,7 +20,8 @@ #ifdef CONFIG_NL80211_TESTMODE void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len); -int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len); +int ath6kl_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, + void *data, int len); #else @@ -29,7 +30,9 @@ static inline void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, { } -static inline int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) +static inline int ath6kl_tm_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + void *data, int len) { return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 277b37ae7126..b7d885020dd7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -3152,7 +3152,9 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, } #ifdef CONFIG_NL80211_TESTMODE -static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len) +static int brcmf_cfg80211_testmode(struct wiphy *wiphy, + struct wireless_dev *wdev, + void *data, int len) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg); diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index b7495c72061c..9ab7a0690d93 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -2081,7 +2081,7 @@ struct cfg80211_update_ft_ies_params { * @mgmt_tx_cancel_wait: Cancel the wait time from transmitting a management * frame on another channel * - * @testmode_cmd: run a test mode command + * @testmode_cmd: run a test mode command; @wdev may be %NULL * @testmode_dump: Implement a test mode dump. The cb->args[2] and up may be * used by the function, but 0 and 1 must not be touched. Additionally, * return error codes other than -ENOBUFS and -ENOENT will terminate the @@ -2290,7 +2290,8 @@ struct cfg80211_ops { void (*rfkill_poll)(struct wiphy *wiphy); #ifdef CONFIG_NL80211_TESTMODE - int (*testmode_cmd)(struct wiphy *wiphy, void *data, int len); + int (*testmode_cmd)(struct wiphy *wiphy, struct wireless_dev *wdev, + void *data, int len); int (*testmode_dump)(struct wiphy *wiphy, struct sk_buff *skb, struct netlink_callback *cb, void *data, int len); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 44449ceb7966..c77916ffe74c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2300,7 +2300,9 @@ static void ieee80211_rfkill_poll(struct wiphy *wiphy) } #ifdef CONFIG_NL80211_TESTMODE -static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) +static int ieee80211_testmode_cmd(struct wiphy *wiphy, + struct wireless_dev *wdev, + void *data, int len) { struct ieee80211_local *local = wiphy_priv(wiphy); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c2a40a2e56bd..334697de5cc0 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6591,19 +6591,30 @@ static struct genl_multicast_group nl80211_testmode_mcgrp = { static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = + __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs); int err; + if (!rdev->ops->testmode_cmd) + return -EOPNOTSUPP; + + if (IS_ERR(wdev)) { + err = PTR_ERR(wdev); + if (err != -EINVAL) + return err; + wdev = NULL; + } else if (wdev->wiphy != &rdev->wiphy) { + return -EINVAL; + } + if (!info->attrs[NL80211_ATTR_TESTDATA]) return -EINVAL; - err = -EOPNOTSUPP; - if (rdev->ops->testmode_cmd) { - rdev->testmode_info = info; - err = rdev_testmode_cmd(rdev, + rdev->testmode_info = info; + err = rdev_testmode_cmd(rdev, wdev, nla_data(info->attrs[NL80211_ATTR_TESTDATA]), nla_len(info->attrs[NL80211_ATTR_TESTDATA])); - rdev->testmode_info = NULL; - } + rdev->testmode_info = NULL; return err; } diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index de870d4d0bcc..37ce9fdfe934 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -516,11 +516,12 @@ static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev) #ifdef CONFIG_NL80211_TESTMODE static inline int rdev_testmode_cmd(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, void *data, int len) { int ret; - trace_rdev_testmode_cmd(&rdev->wiphy); - ret = rdev->ops->testmode_cmd(&rdev->wiphy, data, len); + trace_rdev_testmode_cmd(&rdev->wiphy, wdev); + ret = rdev->ops->testmode_cmd(&rdev->wiphy, wdev, data, len); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index f0ebdcd394ef..ba5f0d6614d5 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1293,15 +1293,17 @@ TRACE_EVENT(rdev_return_int_int, #ifdef CONFIG_NL80211_TESTMODE TRACE_EVENT(rdev_testmode_cmd, - TP_PROTO(struct wiphy *wiphy), - TP_ARGS(wiphy), + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev), TP_STRUCT__entry( WIPHY_ENTRY + WDEV_ENTRY ), TP_fast_assign( WIPHY_ASSIGN; + WDEV_ASSIGN; ), - TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG) + TP_printk(WIPHY_PR_FMT WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG) ); TRACE_EVENT(rdev_testmode_dump, -- cgit v1.2.3-71-gd317 From 52981cd79461e47fe683febfcbd3d380c72b1c6c Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Wed, 31 Jul 2013 18:06:22 +0300 Subject: mac80211: add vif to testmode cmd Pass the wdev from cfg80211 on to the driver as the vif if given and it's valid for the driver. Signed-off-by: David Spinadel Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 1 + drivers/net/wireless/ti/wlcore/testmode.c | 3 ++- drivers/net/wireless/ti/wlcore/testmode.h | 3 ++- include/net/mac80211.h | 7 ++++--- net/mac80211/cfg.c | 11 ++++++++++- 5 files changed, 19 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 7b2a6229eedb..a0d2aacd5e09 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1364,6 +1364,7 @@ static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = { static int hwsim_fops_ps_write(void *dat, u64 val); static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, void *data, int len) { struct mac80211_hwsim_data *hwsim = hw->priv; diff --git a/drivers/net/wireless/ti/wlcore/testmode.c b/drivers/net/wireless/ti/wlcore/testmode.c index f3442762d884..527590f2adfb 100644 --- a/drivers/net/wireless/ti/wlcore/testmode.c +++ b/drivers/net/wireless/ti/wlcore/testmode.c @@ -356,7 +356,8 @@ out: return ret; } -int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len) +int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len) { struct wl1271 *wl = hw->priv; struct nlattr *tb[WL1271_TM_ATTR_MAX + 1]; diff --git a/drivers/net/wireless/ti/wlcore/testmode.h b/drivers/net/wireless/ti/wlcore/testmode.h index 8071654259ea..61d8434d859a 100644 --- a/drivers/net/wireless/ti/wlcore/testmode.h +++ b/drivers/net/wireless/ti/wlcore/testmode.h @@ -26,6 +26,7 @@ #include -int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len); +int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len); #endif /* __WL1271_TESTMODE_H__ */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index b70c00111323..df93c77c97ab 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2516,8 +2516,8 @@ enum ieee80211_roc_type { * in IEEE 802.11-2007 section 17.3.8.6 and modify ACK timeout * accordingly. This callback is not required and may sleep. * - * @testmode_cmd: Implement a cfg80211 test mode command. - * The callback can sleep. + * @testmode_cmd: Implement a cfg80211 test mode command. The passed @vif may + * be %NULL. The callback can sleep. * @testmode_dump: Implement a cfg80211 test mode dump. The callback can sleep. * * @flush: Flush all pending frames from the hardware queue, making sure @@ -2778,7 +2778,8 @@ struct ieee80211_ops { void (*rfkill_poll)(struct ieee80211_hw *hw); void (*set_coverage_class)(struct ieee80211_hw *hw, u8 coverage_class); #ifdef CONFIG_NL80211_TESTMODE - int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len); + int (*testmode_cmd)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len); int (*testmode_dump)(struct ieee80211_hw *hw, struct sk_buff *skb, struct netlink_callback *cb, void *data, int len); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c77916ffe74c..7aa38ce0b524 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2305,11 +2305,20 @@ static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len) { struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_vif *vif = NULL; if (!local->ops->testmode_cmd) return -EOPNOTSUPP; - return local->ops->testmode_cmd(&local->hw, data, len); + if (wdev) { + struct ieee80211_sub_if_data *sdata; + + sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + if (sdata->flags & IEEE80211_SDATA_IN_DRIVER) + vif = &sdata->vif; + } + + return local->ops->testmode_cmd(&local->hw, vif, data, len); } static int ieee80211_testmode_dump(struct wiphy *wiphy, -- cgit v1.2.3-71-gd317 From 6bdaa63c2957ac04e8d596880f732b79f9c06c3c Mon Sep 17 00:00:00 2001 From: Aravind Gopalakrishnan Date: Fri, 2 Aug 2013 17:43:02 -0500 Subject: pci_ids: Add PCI device ID functions 3 and 4 for newer F15h models. Add PCI device IDs for AMD F15h, model 30h. They will be used in amd_nb.c and amd64_edac.c Signed-off-by: Aravind Gopalakrishnan Signed-off-by: Borislav Petkov --- include/linux/pci_ids.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 3bed2e89611b..d1fe5d00140a 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -518,6 +518,8 @@ #define PCI_DEVICE_ID_AMD_11H_NB_MISC 0x1303 #define PCI_DEVICE_ID_AMD_11H_NB_LINK 0x1304 #define PCI_DEVICE_ID_AMD_15H_M10H_F3 0x1403 +#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F3 0x141d +#define PCI_DEVICE_ID_AMD_15H_M30H_NB_F4 0x141e #define PCI_DEVICE_ID_AMD_15H_NB_F0 0x1600 #define PCI_DEVICE_ID_AMD_15H_NB_F1 0x1601 #define PCI_DEVICE_ID_AMD_15H_NB_F2 0x1602 -- cgit v1.2.3-71-gd317 From 4287d824f265451cd10f6d20266b27a207a6cdd7 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 9 Aug 2013 22:27:06 +0200 Subject: PCI: use weak functions for MSI arch-specific functions Until now, the MSI architecture-specific functions could be overloaded using a fairly complex set of #define and compile-time conditionals. In order to prepare for the introduction of the msi_chip infrastructure, it is desirable to switch all those functions to use the 'weak' mechanism. This commit converts all the architectures that were overidding those MSI functions to use the new strategy. Note that we keep two separate, non-weak, functions default_teardown_msi_irqs() and default_restore_msi_irqs() for the default behavior of the arch_teardown_msi_irqs() and arch_restore_msi_irqs(), as the default behavior is needed by x86 PCI code. Signed-off-by: Thomas Petazzoni Acked-by: Bjorn Helgaas Acked-by: Benjamin Herrenschmidt Tested-by: Daniel Price Tested-by: Thierry Reding Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: linuxppc-dev@lists.ozlabs.org Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: linux390@de.ibm.com Cc: linux-s390@vger.kernel.org Cc: Thomas Gleixner Cc: Ingo Molnar Cc: H. Peter Anvin Cc: x86@kernel.org Cc: Russell King Cc: Tony Luck Cc: Fenghua Yu Cc: linux-ia64@vger.kernel.org Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: David S. Miller Cc: sparclinux@vger.kernel.org Cc: Chris Metcalf Signed-off-by: Jason Cooper --- arch/mips/include/asm/pci.h | 5 ----- arch/powerpc/include/asm/pci.h | 5 ----- arch/s390/include/asm/pci.h | 4 ---- arch/x86/include/asm/pci.h | 30 -------------------------- arch/x86/kernel/x86_init.c | 24 +++++++++++++++++++++ drivers/pci/msi.c | 48 +++++++++++++++++++++--------------------- include/linux/msi.h | 8 ++++++- 7 files changed, 55 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/arch/mips/include/asm/pci.h b/arch/mips/include/asm/pci.h index fa8e0aa250ca..f194c08bd057 100644 --- a/arch/mips/include/asm/pci.h +++ b/arch/mips/include/asm/pci.h @@ -136,11 +136,6 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) return channel ? 15 : 14; } -#ifdef CONFIG_CPU_CAVIUM_OCTEON -/* MSI arch hook for OCTEON */ -#define arch_setup_msi_irqs arch_setup_msi_irqs -#endif - extern char * (*pcibios_plat_setup)(char *str); #ifdef CONFIG_OF diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 6653f2743c4e..95145a15c708 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -113,11 +113,6 @@ extern int pci_domain_nr(struct pci_bus *bus); /* Decide whether to display the domain number in /proc */ extern int pci_proc_domain(struct pci_bus *bus); -/* MSI arch hooks */ -#define arch_setup_msi_irqs arch_setup_msi_irqs -#define arch_teardown_msi_irqs arch_teardown_msi_irqs -#define arch_msi_check_device arch_msi_check_device - struct vm_area_struct; /* Map a range of PCI memory or I/O space for a device into user space */ int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index 6e577ba0e5da..262b91bb8811 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -21,10 +21,6 @@ void pci_iounmap(struct pci_dev *, void __iomem *); int pci_domain_nr(struct pci_bus *); int pci_proc_domain(struct pci_bus *); -/* MSI arch hooks */ -#define arch_setup_msi_irqs arch_setup_msi_irqs -#define arch_teardown_msi_irqs arch_teardown_msi_irqs - #define ZPCI_BUS_NR 0 /* default bus number */ #define ZPCI_DEVFN 0 /* default device number */ diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index d9e9e6c7ed32..7d7443283a9d 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -100,29 +100,6 @@ static inline void early_quirks(void) { } extern void pci_iommu_alloc(void); #ifdef CONFIG_PCI_MSI -/* MSI arch specific hooks */ -static inline int x86_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) -{ - return x86_msi.setup_msi_irqs(dev, nvec, type); -} - -static inline void x86_teardown_msi_irqs(struct pci_dev *dev) -{ - x86_msi.teardown_msi_irqs(dev); -} - -static inline void x86_teardown_msi_irq(unsigned int irq) -{ - x86_msi.teardown_msi_irq(irq); -} -static inline void x86_restore_msi_irqs(struct pci_dev *dev, int irq) -{ - x86_msi.restore_msi_irqs(dev, irq); -} -#define arch_setup_msi_irqs x86_setup_msi_irqs -#define arch_teardown_msi_irqs x86_teardown_msi_irqs -#define arch_teardown_msi_irq x86_teardown_msi_irq -#define arch_restore_msi_irqs x86_restore_msi_irqs /* implemented in arch/x86/kernel/apic/io_apic. */ struct msi_desc; int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type); @@ -130,16 +107,9 @@ void native_teardown_msi_irq(unsigned int irq); void native_restore_msi_irqs(struct pci_dev *dev, int irq); int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc, unsigned int irq_base, unsigned int irq_offset); -/* default to the implementation in drivers/lib/msi.c */ -#define HAVE_DEFAULT_MSI_TEARDOWN_IRQS -#define HAVE_DEFAULT_MSI_RESTORE_IRQS -void default_teardown_msi_irqs(struct pci_dev *dev); -void default_restore_msi_irqs(struct pci_dev *dev, int irq); #else #define native_setup_msi_irqs NULL #define native_teardown_msi_irq NULL -#define default_teardown_msi_irqs NULL -#define default_restore_msi_irqs NULL #endif #define PCI_DMA_BUS_IS_PHYS (dma_ops->is_phys) diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c index 45a14dbbddaf..5587f991d111 100644 --- a/arch/x86/kernel/x86_init.c +++ b/arch/x86/kernel/x86_init.c @@ -107,6 +107,8 @@ struct x86_platform_ops x86_platform = { }; EXPORT_SYMBOL_GPL(x86_platform); + +#if defined(CONFIG_PCI_MSI) struct x86_msi_ops x86_msi = { .setup_msi_irqs = native_setup_msi_irqs, .compose_msi_msg = native_compose_msi_msg, @@ -116,6 +118,28 @@ struct x86_msi_ops x86_msi = { .setup_hpet_msi = default_setup_hpet_msi, }; +/* MSI arch specific hooks */ +int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +{ + return x86_msi.setup_msi_irqs(dev, nvec, type); +} + +void arch_teardown_msi_irqs(struct pci_dev *dev) +{ + x86_msi.teardown_msi_irqs(dev); +} + +void arch_teardown_msi_irq(unsigned int irq) +{ + x86_msi.teardown_msi_irq(irq); +} + +void arch_restore_msi_irqs(struct pci_dev *dev, int irq) +{ + x86_msi.restore_msi_irqs(dev, irq); +} +#endif + struct x86_io_apic_ops x86_io_apic_ops = { .init = native_io_apic_init_mappings, .read = native_io_apic_read, diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index aca7578b05e5..823c3861c4d8 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -30,20 +30,21 @@ static int pci_msi_enable = 1; /* Arch hooks */ -#ifndef arch_msi_check_device -int arch_msi_check_device(struct pci_dev *dev, int nvec, int type) +int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) { - return 0; + return -EINVAL; } -#endif -#ifndef arch_setup_msi_irqs -# define arch_setup_msi_irqs default_setup_msi_irqs -# define HAVE_DEFAULT_MSI_SETUP_IRQS -#endif +void __weak arch_teardown_msi_irq(unsigned int irq) +{ +} -#ifdef HAVE_DEFAULT_MSI_SETUP_IRQS -int default_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +int __weak arch_msi_check_device(struct pci_dev *dev, int nvec, int type) +{ + return 0; +} + +int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) { struct msi_desc *entry; int ret; @@ -65,14 +66,11 @@ int default_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) return 0; } -#endif - -#ifndef arch_teardown_msi_irqs -# define arch_teardown_msi_irqs default_teardown_msi_irqs -# define HAVE_DEFAULT_MSI_TEARDOWN_IRQS -#endif -#ifdef HAVE_DEFAULT_MSI_TEARDOWN_IRQS +/* + * We have a default implementation available as a separate non-weak + * function, as it is used by the Xen x86 PCI code + */ void default_teardown_msi_irqs(struct pci_dev *dev) { struct msi_desc *entry; @@ -89,14 +87,12 @@ void default_teardown_msi_irqs(struct pci_dev *dev) arch_teardown_msi_irq(entry->irq + i); } } -#endif -#ifndef arch_restore_msi_irqs -# define arch_restore_msi_irqs default_restore_msi_irqs -# define HAVE_DEFAULT_MSI_RESTORE_IRQS -#endif +void __weak arch_teardown_msi_irqs(struct pci_dev *dev) +{ + return default_teardown_msi_irqs(dev); +} -#ifdef HAVE_DEFAULT_MSI_RESTORE_IRQS void default_restore_msi_irqs(struct pci_dev *dev, int irq) { struct msi_desc *entry; @@ -114,7 +110,11 @@ void default_restore_msi_irqs(struct pci_dev *dev, int irq) if (entry) write_msi_msg(irq, &entry->msg); } -#endif + +void __weak arch_restore_msi_irqs(struct pci_dev *dev, int irq) +{ + return default_restore_msi_irqs(dev, irq); +} static void msi_set_enable(struct pci_dev *dev, int enable) { diff --git a/include/linux/msi.h b/include/linux/msi.h index ee66f3a12fb6..271dfd14a05f 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -51,12 +51,18 @@ struct msi_desc { }; /* - * The arch hook for setup up msi irqs + * The arch hooks to setup up msi irqs. Those functions are + * implemented as weak symbols so that they /can/ be overriden by + * architecture specific code if needed. */ int arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc); void arch_teardown_msi_irq(unsigned int irq); int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type); void arch_teardown_msi_irqs(struct pci_dev *dev); int arch_msi_check_device(struct pci_dev* dev, int nvec, int type); +void arch_restore_msi_irqs(struct pci_dev *dev, int irq); + +void default_teardown_msi_irqs(struct pci_dev *dev); +void default_restore_msi_irqs(struct pci_dev *dev, int irq); #endif /* LINUX_MSI_H */ -- cgit v1.2.3-71-gd317 From 0cbdcfcf427b63b9670e56760ef5e67cd7081b35 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 9 Aug 2013 22:27:08 +0200 Subject: PCI: Introduce new MSI chip infrastructure The new struct msi_chip is used to associated an MSI controller with a PCI bus. It is automatically handed down from the root to its children during bus enumeration. This patch provides default (weak) implementations for the architecture- specific MSI functions (arch_setup_msi_irq(), arch_teardown_msi_irq() and arch_msi_check_device()) which check if a PCI device's bus has an attached MSI chip and forward the call appropriately. Signed-off-by: Thierry Reding Signed-off-by: Thomas Petazzoni Acked-by: Bjorn Helgaas Tested-by: Daniel Price Tested-by: Thierry Reding Signed-off-by: Jason Cooper --- drivers/pci/msi.c | 27 +++++++++++++++++++++++++-- drivers/pci/probe.c | 1 + include/linux/msi.h | 11 +++++++++++ include/linux/pci.h | 1 + 4 files changed, 38 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 823c3861c4d8..2837285c4e83 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -32,16 +32,39 @@ static int pci_msi_enable = 1; int __weak arch_setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc) { - return -EINVAL; + struct msi_chip *chip = dev->bus->msi; + int err; + + if (!chip || !chip->setup_irq) + return -EINVAL; + + err = chip->setup_irq(chip, dev, desc); + if (err < 0) + return err; + + irq_set_chip_data(desc->irq, chip); + + return 0; } void __weak arch_teardown_msi_irq(unsigned int irq) { + struct msi_chip *chip = irq_get_chip_data(irq); + + if (!chip || !chip->teardown_irq) + return; + + chip->teardown_irq(chip, irq); } int __weak arch_msi_check_device(struct pci_dev *dev, int nvec, int type) { - return 0; + struct msi_chip *chip = dev->bus->msi; + + if (!chip || !chip->check_device) + return 0; + + return chip->check_device(chip, dev, nvec, type); } int __weak arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 46ada5c098eb..b8eaa8167849 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -666,6 +666,7 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, child->parent = parent; child->ops = parent->ops; + child->msi = parent->msi; child->sysdata = parent->sysdata; child->bus_flags = parent->bus_flags; diff --git a/include/linux/msi.h b/include/linux/msi.h index 271dfd14a05f..090ddadbc838 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -65,4 +65,15 @@ void arch_restore_msi_irqs(struct pci_dev *dev, int irq); void default_teardown_msi_irqs(struct pci_dev *dev); void default_restore_msi_irqs(struct pci_dev *dev, int irq); +struct msi_chip { + struct module *owner; + struct device *dev; + + int (*setup_irq)(struct msi_chip *chip, struct pci_dev *dev, + struct msi_desc *desc); + void (*teardown_irq)(struct msi_chip *chip, unsigned int irq); + int (*check_device)(struct msi_chip *chip, struct pci_dev *dev, + int nvec, int type); +}; + #endif /* LINUX_MSI_H */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 0fd1f1582fa1..4044e3c00609 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -433,6 +433,7 @@ struct pci_bus { struct resource busn_res; /* bus numbers routed to this bus */ struct pci_ops *ops; /* configuration access functions */ + struct msi_chip *msi; /* MSI controller */ void *sysdata; /* hook for sys-specific extension */ struct proc_dir_entry *procdir; /* directory entry in /proc/bus/pci */ -- cgit v1.2.3-71-gd317 From 0d5a6db3aa46faafd0212650bfd709d51240d9da Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 9 Aug 2013 22:27:09 +0200 Subject: of: pci: add registry of MSI chips This commit adds a very basic registry of msi_chip structures, so that an IRQ controller driver can register an msi_chip, and a PCIe host controller can find it, based on a 'struct device_node'. Signed-off-by: Thomas Petazzoni Acked-by: Benjamin Herrenschmidt Acked-by: Rob Herring Signed-off-by: Jason Cooper --- drivers/of/of_pci.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/msi.h | 2 ++ include/linux/of_pci.h | 12 ++++++++++++ 3 files changed, 59 insertions(+) (limited to 'include') diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 42c687a820ac..e5ca00893c0c 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -89,3 +89,48 @@ int of_pci_parse_bus_range(struct device_node *node, struct resource *res) return 0; } EXPORT_SYMBOL_GPL(of_pci_parse_bus_range); + +#ifdef CONFIG_PCI_MSI + +static LIST_HEAD(of_pci_msi_chip_list); +static DEFINE_MUTEX(of_pci_msi_chip_mutex); + +int of_pci_msi_chip_add(struct msi_chip *chip) +{ + if (!of_property_read_bool(chip->of_node, "msi-controller")) + return -EINVAL; + + mutex_lock(&of_pci_msi_chip_mutex); + list_add(&chip->list, &of_pci_msi_chip_list); + mutex_unlock(&of_pci_msi_chip_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(of_pci_msi_chip_add); + +void of_pci_msi_chip_remove(struct msi_chip *chip) +{ + mutex_lock(&of_pci_msi_chip_mutex); + list_del(&chip->list); + mutex_unlock(&of_pci_msi_chip_mutex); +} +EXPORT_SYMBOL_GPL(of_pci_msi_chip_remove); + +struct msi_chip *of_pci_find_msi_chip_by_node(struct device_node *of_node) +{ + struct msi_chip *c; + + mutex_lock(&of_pci_msi_chip_mutex); + list_for_each_entry(c, &of_pci_msi_chip_list, list) { + if (c->of_node == of_node) { + mutex_unlock(&of_pci_msi_chip_mutex); + return c; + } + } + mutex_unlock(&of_pci_msi_chip_mutex); + + return NULL; +} +EXPORT_SYMBOL_GPL(of_pci_find_msi_chip_by_node); + +#endif /* CONFIG_PCI_MSI */ diff --git a/include/linux/msi.h b/include/linux/msi.h index 090ddadbc838..b17ead818aec 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -68,6 +68,8 @@ void default_restore_msi_irqs(struct pci_dev *dev, int irq); struct msi_chip { struct module *owner; struct device *dev; + struct device_node *of_node; + struct list_head list; int (*setup_irq)(struct msi_chip *chip, struct pci_dev *dev, struct msi_desc *desc); diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h index 7a04826018c0..fd9c408631a0 100644 --- a/include/linux/of_pci.h +++ b/include/linux/of_pci.h @@ -2,6 +2,7 @@ #define __OF_PCI_H #include +#include struct pci_dev; struct of_irq; @@ -13,4 +14,15 @@ struct device_node *of_pci_find_child_device(struct device_node *parent, int of_pci_get_devfn(struct device_node *np); int of_pci_parse_bus_range(struct device_node *node, struct resource *res); +#if defined(CONFIG_OF) && defined(CONFIG_PCI_MSI) +int of_pci_msi_chip_add(struct msi_chip *chip); +void of_pci_msi_chip_remove(struct msi_chip *chip); +struct msi_chip *of_pci_find_msi_chip_by_node(struct device_node *of_node); +#else +static inline int of_pci_msi_chip_add(struct msi_chip *chip) { return -EINVAL; } +static inline void of_pci_msi_chip_remove(struct msi_chip *chip) { } +static inline struct msi_chip * +of_pci_find_msi_chip_by_node(struct device_node *of_node) { return NULL; } +#endif + #endif -- cgit v1.2.3-71-gd317 From 5c19e95216b93b0d29c6a4887e69a980edc6fc81 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 5 Aug 2013 14:40:44 +0200 Subject: OF: Add helper for matching against linux,stdout-path devicetrees may have a linux,stdout-path property in the chosen node describing the console device. This adds a helper function to match a device against this property so a driver can call add_preferred_console for a matching device. Signed-off-by: Sascha Hauer Acked-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- drivers/of/base.c | 26 ++++++++++++++++++++++++++ include/linux/of.h | 7 +++++++ 2 files changed, 33 insertions(+) (limited to 'include') diff --git a/drivers/of/base.c b/drivers/of/base.c index 5c5427918eb2..1c3c79c79742 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -32,6 +32,7 @@ struct device_node *of_allnodes; EXPORT_SYMBOL(of_allnodes); struct device_node *of_chosen; struct device_node *of_aliases; +static struct device_node *of_stdout; DEFINE_MUTEX(of_aliases_mutex); @@ -1595,6 +1596,15 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) of_chosen = of_find_node_by_path("/chosen"); if (of_chosen == NULL) of_chosen = of_find_node_by_path("/chosen@0"); + + if (of_chosen) { + const char *name; + + name = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (name) + of_stdout = of_find_node_by_path(name); + } + of_aliases = of_find_node_by_path("/aliases"); if (!of_aliases) return; @@ -1703,3 +1713,19 @@ const char *of_prop_next_string(struct property *prop, const char *cur) return curv; } EXPORT_SYMBOL_GPL(of_prop_next_string); + +/** + * of_device_is_stdout_path - check if a device node matches the + * linux,stdout-path property + * + * Check if this device node matches the linux,stdout-path property + * in the chosen node. return true if yes, false otherwise. + */ +int of_device_is_stdout_path(struct device_node *dn) +{ + if (!of_stdout) + return false; + + return of_stdout == dn; +} +EXPORT_SYMBOL_GPL(of_device_is_stdout_path); diff --git a/include/linux/of.h b/include/linux/of.h index 1fd08ca23106..429e16801858 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -343,6 +343,8 @@ const char *of_prop_next_string(struct property *prop, const char *cur); s; \ s = of_prop_next_string(prop, s)) +int of_device_is_stdout_path(struct device_node *dn); + #else /* CONFIG_OF */ static inline const char* of_node_full_name(struct device_node *np) @@ -505,6 +507,11 @@ static inline int of_machine_is_compatible(const char *compat) return 0; } +static inline int of_device_is_stdout_path(struct device_node *dn) +{ + return 0; +} + #define of_match_ptr(_ptr) NULL #define of_match_node(_matches, _node) NULL #define of_property_for_each_u32(np, propname, prop, p, u) \ -- cgit v1.2.3-71-gd317 From 3e635202ce40e4d7ff3fafc18db70c5d28cc6622 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Mon, 12 Aug 2013 16:06:51 +0300 Subject: usb: phy: tegra: Tegra30 support The Tegra30 USB PHY is a bit different than the Tegra20 PHY: - The EHCI controller supports the HOSTPC register extension, and some of the fields that the PHY needs to modify (PHCD and PTS) have moved to the new HOSTPC register. - Some of the UTMI PLL configuration registers have moved from the USB register space to the Clock-And-Reset controller space. In Tegra30 the clock driver is responsible for configuring the UTMI PLL. - The USBMODE register must be explicitly written to enter host mode. - Certain PHY parameters need to be programmed for optimal signal quality. Support for this will be added in the next patch. The new tegra_phy_soc_config structure is added to describe the differences between the SoCs. Signed-off-by: Tuomas Tynkkynen Tested-by: Stephen Warren Reviewed-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 121 +++++++++++++++++++++++++++++--------- include/linux/usb/tegra_usb_phy.h | 19 ++++++ 2 files changed, 112 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index ebbf85fdfc7f..1ad184af7601 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -39,11 +40,16 @@ #define ULPI_VIEWPORT 0x170 -/* PORTSC registers */ +/* PORTSC PTS/PHCD bits, Tegra20 only */ #define TEGRA_USB_PORTSC1 0x184 #define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) #define TEGRA_USB_PORTSC1_PHCD (1 << 23) +/* HOSTPC1 PTS/PHCD bits, Tegra30 and above */ +#define TEGRA_USB_HOSTPC1_DEVLC 0x1b4 +#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) +#define TEGRA_USB_HOSTPC1_DEVLC_PHCD (1 << 22) + /* Bits of PORTSC1, which will get cleared by writing 1 into them */ #define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) @@ -141,6 +147,12 @@ #define UTMIP_BIAS_CFG1 0x83c #define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) +/* For Tegra30 and above only, the address is different in Tegra20 */ +#define USB_USBMODE 0x1f8 +#define USB_USBMODE_MASK (3 << 0) +#define USB_USBMODE_HOST (3 << 0) +#define USB_USBMODE_DEVICE (2 << 0) + static DEFINE_SPINLOCK(utmip_pad_lock); static int utmip_pad_count; @@ -193,10 +205,17 @@ static void set_pts(struct tegra_usb_phy *phy, u8 pts_val) void __iomem *base = phy->regs; unsigned long val; - val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS; - val &= ~TEGRA_USB_PORTSC1_PTS(3); - val |= TEGRA_USB_PORTSC1_PTS(pts_val & 3); - writel(val, base + TEGRA_USB_PORTSC1); + if (phy->soc_config->has_hostpc) { + val = readl(base + TEGRA_USB_HOSTPC1_DEVLC); + val &= ~TEGRA_USB_HOSTPC1_DEVLC_PTS(~0); + val |= TEGRA_USB_HOSTPC1_DEVLC_PTS(pts_val); + writel(val, base + TEGRA_USB_HOSTPC1_DEVLC); + } else { + val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS; + val &= ~TEGRA_USB_PORTSC1_PTS(~0); + val |= TEGRA_USB_PORTSC1_PTS(pts_val); + writel(val, base + TEGRA_USB_PORTSC1); + } } static void set_phcd(struct tegra_usb_phy *phy, bool enable) @@ -204,12 +223,21 @@ static void set_phcd(struct tegra_usb_phy *phy, bool enable) void __iomem *base = phy->regs; unsigned long val; - val = readl(base + TEGRA_USB_PORTSC1) & ~TEGRA_PORTSC1_RWC_BITS; - if (enable) - val |= TEGRA_USB_PORTSC1_PHCD; - else - val &= ~TEGRA_USB_PORTSC1_PHCD; - writel(val, base + TEGRA_USB_PORTSC1); + if (phy->soc_config->has_hostpc) { + val = readl(base + TEGRA_USB_HOSTPC1_DEVLC); + if (enable) + val |= TEGRA_USB_HOSTPC1_DEVLC_PHCD; + else + val &= ~TEGRA_USB_HOSTPC1_DEVLC_PHCD; + writel(val, base + TEGRA_USB_HOSTPC1_DEVLC); + } else { + val = readl(base + TEGRA_USB_PORTSC1) & ~PORT_RWC_BITS; + if (enable) + val |= TEGRA_USB_PORTSC1_PHCD; + else + val &= ~TEGRA_USB_PORTSC1_PHCD; + writel(val, base + TEGRA_USB_PORTSC1); + } } static int utmip_pad_open(struct tegra_usb_phy *phy) @@ -367,17 +395,21 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE; writel(val, base + UTMIP_MISC_CFG0); - val = readl(base + UTMIP_MISC_CFG1); - val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | UTMIP_PLLU_STABLE_COUNT(~0)); - val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) | - UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count); - writel(val, base + UTMIP_MISC_CFG1); - - val = readl(base + UTMIP_PLL_CFG1); - val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0)); - val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) | - UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); - writel(val, base + UTMIP_PLL_CFG1); + if (!phy->soc_config->utmi_pll_config_in_car_module) { + val = readl(base + UTMIP_MISC_CFG1); + val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) | + UTMIP_PLLU_STABLE_COUNT(~0)); + val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) | + UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count); + writel(val, base + UTMIP_MISC_CFG1); + + val = readl(base + UTMIP_PLL_CFG1); + val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | + UTMIP_PLLU_ENABLE_DLY_COUNT(~0)); + val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) | + UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); + writel(val, base + UTMIP_PLL_CFG1); + } if (phy->mode == USB_DR_MODE_PERIPHERAL) { val = readl(base + USB_SUSP_CTRL); @@ -448,6 +480,16 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) utmi_phy_clk_enable(phy); + if (phy->soc_config->requires_usbmode_setup) { + val = readl(base + USB_USBMODE); + val &= ~USB_USBMODE_MASK; + if (phy->mode == USB_DR_MODE_HOST) + val |= USB_USBMODE_HOST; + else + val |= USB_USBMODE_DEVICE; + writel(val, base + USB_USBMODE); + } + if (!phy->is_legacy_phy) set_pts(phy, 0); @@ -864,8 +906,30 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, return 0; } +static const struct tegra_phy_soc_config tegra20_soc_config = { + .utmi_pll_config_in_car_module = false, + .has_hostpc = false, + .requires_usbmode_setup = false, + .requires_extra_tuning_parameters = false, +}; + +static const struct tegra_phy_soc_config tegra30_soc_config = { + .utmi_pll_config_in_car_module = true, + .has_hostpc = true, + .requires_usbmode_setup = true, + .requires_extra_tuning_parameters = true, +}; + +static struct of_device_id tegra_usb_phy_id_table[] = { + { .compatible = "nvidia,tegra30-usb-phy", .data = &tegra30_soc_config }, + { .compatible = "nvidia,tegra20-usb-phy", .data = &tegra20_soc_config }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table); + static int tegra_usb_phy_probe(struct platform_device *pdev) { + const struct of_device_id *match; struct resource *res; struct tegra_usb_phy *tegra_phy = NULL; struct device_node *np = pdev->dev.of_node; @@ -878,6 +942,13 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) return -ENOMEM; } + match = of_match_device(tegra_usb_phy_id_table, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + tegra_phy->soc_config = match->data; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "Failed to get I/O memory\n"); @@ -968,12 +1039,6 @@ static int tegra_usb_phy_remove(struct platform_device *pdev) return 0; } -static struct of_device_id tegra_usb_phy_id_table[] = { - { .compatible = "nvidia,tegra20-usb-phy", }, - { }, -}; -MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table); - static struct platform_driver tegra_usb_phy_driver = { .probe = tegra_usb_phy_probe, .remove = tegra_usb_phy_remove, diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index d3db274610a1..d3a63c354db9 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -18,6 +18,24 @@ #include #include +/* + * utmi_pll_config_in_car_module: true if the UTMI PLL configuration registers + * should be set up by clk-tegra, false if by the PHY code + * has_hostpc: true if the USB controller has the HOSTPC extension, which + * changes the location of the PHCD and PTS fields + * requires_usbmode_setup: true if the USBMODE register needs to be set to + * enter host mode + * requires_extra_tuning_parameters: true if xcvr_hsslew, hssquelch_level + * and hsdiscon_level should be set for adequate signal quality + */ + +struct tegra_phy_soc_config { + bool utmi_pll_config_in_car_module; + bool has_hostpc; + bool requires_usbmode_setup; + bool requires_extra_tuning_parameters; +}; + struct tegra_utmip_config { u8 hssync_start_delay; u8 elastic_limit; @@ -47,6 +65,7 @@ struct tegra_usb_phy { struct regulator *vbus; enum usb_dr_mode mode; void *config; + const struct tegra_phy_soc_config *soc_config; struct usb_phy *ulpi; struct usb_phy u_phy; bool is_legacy_phy; -- cgit v1.2.3-71-gd317 From e497a24d8e18af510879b2ae059ee20a4a58eae8 Mon Sep 17 00:00:00 2001 From: Tuomas Tynkkynen Date: Mon, 12 Aug 2013 16:06:53 +0300 Subject: usb: phy: tegra: Program new PHY parameters The Tegra30 TRM recommends configuration of certain PHY parameters for optimal quality. Program the following registers based on device tree parameters: - UTMIP_XCVR_HSSLEW: HS slew rate control. - UTMIP_HSSQUELCH_LEVEL: HS squelch detector level - UTMIP_HSDISCON_LEVEL: HS disconnect detector level. These registers exist in Tegra20, but programming them hasn't been necessary, so these parameters won't be set on Tegra20 to keep the device trees backward compatible. Additionally, the UTMIP_XCVR_SETUP parameter can be set from fuses instead of a software-programmed value, as the optimal value can vary between invidual boards. The boolean property nvidia,xcvr-setup-use-fuses can be used to enable this behaviour. Signed-off-by: Tuomas Tynkkynen Tested-by: Stephen Warren Reviewed-by: Stephen Warren Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 81 ++++++++++++++++++++++++++++++--------- include/linux/usb/tegra_usb_phy.h | 4 ++ 2 files changed, 67 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 1ad184af7601..3bfb3d1957c1 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -99,11 +99,15 @@ #define UTMIP_FORCE_PD2_POWERDOWN (1 << 16) #define UTMIP_FORCE_PDZI_POWERDOWN (1 << 18) #define UTMIP_XCVR_LSBIAS_SEL (1 << 21) -#define UTMIP_XCVR_HSSLEW_MSB(x) (((x) & 0x7f) << 25) +#define UTMIP_XCVR_HSSLEW(x) (((x) & 0x3) << 4) +#define UTMIP_XCVR_HSSLEW_MSB(x) ((((x) & 0x1fc) >> 2) << 25) #define UTMIP_BIAS_CFG0 0x80c #define UTMIP_OTGPD (1 << 11) #define UTMIP_BIASPD (1 << 10) +#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0) +#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2) +#define UTMIP_HSDISCON_LEVEL_MSB(x) ((((x) & 0x4) >> 2) << 24) #define UTMIP_HSRX_CFG0 0x810 #define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10) @@ -255,6 +259,7 @@ static void utmip_pad_power_on(struct tegra_usb_phy *phy) { unsigned long val, flags; void __iomem *base = phy->pad_regs; + struct tegra_utmip_config *config = phy->config; clk_prepare_enable(phy->pad_clk); @@ -263,6 +268,16 @@ static void utmip_pad_power_on(struct tegra_usb_phy *phy) if (utmip_pad_count++ == 0) { val = readl(base + UTMIP_BIAS_CFG0); val &= ~(UTMIP_OTGPD | UTMIP_BIASPD); + + if (phy->soc_config->requires_extra_tuning_parameters) { + val &= ~(UTMIP_HSSQUELCH_LEVEL(~0) | + UTMIP_HSDISCON_LEVEL(~0) | + UTMIP_HSDISCON_LEVEL_MSB(~0)); + + val |= UTMIP_HSSQUELCH_LEVEL(config->hssquelch_level); + val |= UTMIP_HSDISCON_LEVEL(config->hsdiscon_level); + val |= UTMIP_HSDISCON_LEVEL_MSB(config->hsdiscon_level); + } writel(val, base + UTMIP_BIAS_CFG0); } @@ -431,12 +446,20 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN | UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_LSBIAS_SEL | UTMIP_XCVR_SETUP(~0) | UTMIP_XCVR_SETUP_MSB(~0) | - UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0) | - UTMIP_XCVR_HSSLEW_MSB(~0)); - val |= UTMIP_XCVR_SETUP(config->xcvr_setup); - val |= UTMIP_XCVR_SETUP_MSB(config->xcvr_setup); + UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0)); + + if (!config->xcvr_setup_use_fuses) { + val |= UTMIP_XCVR_SETUP(config->xcvr_setup); + val |= UTMIP_XCVR_SETUP_MSB(config->xcvr_setup); + } val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew); val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew); + + if (phy->soc_config->requires_extra_tuning_parameters) { + val &= ~(UTMIP_XCVR_HSSLEW(~0) | UTMIP_XCVR_HSSLEW_MSB(~0)); + val |= UTMIP_XCVR_HSSLEW(config->xcvr_hsslew); + val |= UTMIP_XCVR_HSSLEW_MSB(config->xcvr_hsslew); + } writel(val, base + UTMIP_XCVR_CFG0); val = readl(base + UTMIP_XCVR_CFG1); @@ -450,14 +473,14 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val |= UTMIP_BIAS_PDTRK_COUNT(0x5); writel(val, base + UTMIP_BIAS_CFG1); - if (phy->is_legacy_phy) { - val = readl(base + UTMIP_SPARE_CFG0); - if (phy->mode == USB_DR_MODE_PERIPHERAL) - val &= ~FUSE_SETUP_SEL; - else - val |= FUSE_SETUP_SEL; - writel(val, base + UTMIP_SPARE_CFG0); - } else { + val = readl(base + UTMIP_SPARE_CFG0); + if (config->xcvr_setup_use_fuses) + val |= FUSE_SETUP_SEL; + else + val &= ~FUSE_SETUP_SEL; + writel(val, base + UTMIP_SPARE_CFG0); + + if (!phy->is_legacy_phy) { val = readl(base + USB_SUSP_CTRL); val |= UTMIP_PHY_ENABLE; writel(val, base + USB_SUSP_CTRL); @@ -888,11 +911,6 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, if (err < 0) return err; - err = read_utmi_param(pdev, "nvidia,xcvr-setup", - &config->xcvr_setup); - if (err < 0) - return err; - err = read_utmi_param(pdev, "nvidia,xcvr-lsfslew", &config->xcvr_lsfslew); if (err < 0) @@ -903,6 +921,33 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, if (err < 0) return err; + if (tegra_phy->soc_config->requires_extra_tuning_parameters) { + err = read_utmi_param(pdev, "nvidia,xcvr-hsslew", + &config->xcvr_hsslew); + if (err < 0) + return err; + + err = read_utmi_param(pdev, "nvidia,hssquelch-level", + &config->hssquelch_level); + if (err < 0) + return err; + + err = read_utmi_param(pdev, "nvidia,hsdiscon-level", + &config->hsdiscon_level); + if (err < 0) + return err; + } + + config->xcvr_setup_use_fuses = of_property_read_bool( + pdev->dev.of_node, "nvidia,xcvr-setup-use-fuses"); + + if (!config->xcvr_setup_use_fuses) { + err = read_utmi_param(pdev, "nvidia,xcvr-setup", + &config->xcvr_setup); + if (err < 0) + return err; + } + return 0; } diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index d3a63c354db9..1de16c324ec8 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -41,9 +41,13 @@ struct tegra_utmip_config { u8 elastic_limit; u8 idle_wait_delay; u8 term_range_adj; + bool xcvr_setup_use_fuses; u8 xcvr_setup; u8 xcvr_lsfslew; u8 xcvr_lsrslew; + u8 xcvr_hsslew; + u8 hssquelch_level; + u8 hsdiscon_level; }; enum tegra_usb_phy_port_speed { -- cgit v1.2.3-71-gd317 From 94dfd7edfd5c9b605caf7b562de7a813d216e011 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 3 Jul 2013 22:53:07 +0800 Subject: USB: HCD: support giveback of URB in tasklet context This patch implements the mechanism of giveback of URB in tasklet context, so that hardware interrupt handling time for usb host controller can be saved much, and HCD interrupt handling can be simplified. Motivations: 1), on some arch(such as ARM), DMA mapping/unmapping is a bit time-consuming, for example: when accessing usb mass storage via EHCI on pandaboard, the common length of transfer buffer is 120KB, the time consumed on DMA unmapping may reach hundreds of microseconds; even on A15 based box, the time is still about scores of microseconds 2), on some arch, reading DMA coherent memoery is very time-consuming, the most common example is usb video class driver[1] 3), driver's complete() callback may do much things which is driver specific, so the time is consumed unnecessarily in hardware irq context. 4), running driver's complete() callback in hardware irq context causes that host controller driver has to release its lock in interrupt handler, so reacquiring the lock after return may busy wait a while and increase interrupt handling time. More seriously, releasing the HCD lock makes HCD becoming quite complicated to deal with introduced races. So the patch proposes to run giveback of URB in tasklet context, then time consumed in HCD irq handling doesn't depend on drivers' complete and DMA mapping/unmapping any more, also we can simplify HCD since the HCD lock isn't needed to be released during irq handling. The patch should be reasonable and doable: 1), for drivers, they don't care if the complete() is called in hard irq context or softirq context 2), the biggest change is the situation in which usb_submit_urb() is called in complete() callback, so the introduced tasklet schedule delay might be a con, but it shouldn't be a big deal: - control/bulk asynchronous transfer isn't sensitive to schedule delay - the patch schedules giveback of periodic URBs using tasklet_hi_schedule, so the introduced delay should be very small - for ISOC transfer, generally, drivers submit several URBs concurrently to avoid interrupt delay, so it is OK with the little schedule delay. - for interrupt transfer, generally, drivers only submit one URB at the same time, but interrupt transfer is often used in event report, polling, ... situations, and a little delay should be OK. Considered that HCDs may optimize on submitting URB in complete(), the patch may cause the optimization not working, so introduces one flag to mark if the HCD supports to run giveback URB in tasklet context. When all HCDs are ready, the flag can be removed. [1], http://marc.info/?t=136438111600010&r=1&w=2 Cc: Oliver Neukum Acked-by: Alan Stern Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 147 ++++++++++++++++++++++++++++++++++++++---------- include/linux/usb/hcd.h | 17 ++++++ 2 files changed, 134 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index dc1346fee76c..0ffe016f4036 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -696,15 +696,7 @@ error: /* any errors get returned through the urb completion */ spin_lock_irq(&hcd_root_hub_lock); usb_hcd_unlink_urb_from_ep(hcd, urb); - - /* This peculiar use of spinlocks echoes what real HC drivers do. - * Avoiding calls to local_irq_disable/enable makes the code - * RT-friendly. - */ - spin_unlock(&hcd_root_hub_lock); usb_hcd_giveback_urb(hcd, urb, status); - spin_lock(&hcd_root_hub_lock); - spin_unlock_irq(&hcd_root_hub_lock); return 0; } @@ -744,9 +736,7 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) memcpy(urb->transfer_buffer, buffer, length); usb_hcd_unlink_urb_from_ep(hcd, urb); - spin_unlock(&hcd_root_hub_lock); usb_hcd_giveback_urb(hcd, urb, 0); - spin_lock(&hcd_root_hub_lock); } else { length = 0; set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags); @@ -836,10 +826,7 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) if (urb == hcd->status_urb) { hcd->status_urb = NULL; usb_hcd_unlink_urb_from_ep(hcd, urb); - - spin_unlock(&hcd_root_hub_lock); usb_hcd_giveback_urb(hcd, urb, status); - spin_lock(&hcd_root_hub_lock); } } done: @@ -1656,6 +1643,72 @@ int usb_hcd_unlink_urb (struct urb *urb, int status) /*-------------------------------------------------------------------------*/ +static void __usb_hcd_giveback_urb(struct urb *urb) +{ + struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus); + int status = urb->unlinked; + unsigned long flags; + + urb->hcpriv = NULL; + if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) && + urb->actual_length < urb->transfer_buffer_length && + !status)) + status = -EREMOTEIO; + + unmap_urb_for_dma(hcd, urb); + usbmon_urb_complete(&hcd->self, urb, status); + usb_unanchor_urb(urb); + + /* pass ownership to the completion handler */ + urb->status = status; + + /* + * We disable local IRQs here avoid possible deadlock because + * drivers may call spin_lock() to hold lock which might be + * acquired in one hard interrupt handler. + * + * The local_irq_save()/local_irq_restore() around complete() + * will be removed if current USB drivers have been cleaned up + * and no one may trigger the above deadlock situation when + * running complete() in tasklet. + */ + local_irq_save(flags); + urb->complete(urb); + local_irq_restore(flags); + + atomic_dec(&urb->use_count); + if (unlikely(atomic_read(&urb->reject))) + wake_up(&usb_kill_urb_queue); + usb_put_urb(urb); +} + +static void usb_giveback_urb_bh(unsigned long param) +{ + struct giveback_urb_bh *bh = (struct giveback_urb_bh *)param; + struct list_head local_list; + + spin_lock_irq(&bh->lock); + bh->running = true; + restart: + list_replace_init(&bh->head, &local_list); + spin_unlock_irq(&bh->lock); + + while (!list_empty(&local_list)) { + struct urb *urb; + + urb = list_entry(local_list.next, struct urb, urb_list); + list_del_init(&urb->urb_list); + __usb_hcd_giveback_urb(urb); + } + + /* check if there are new URBs to giveback */ + spin_lock_irq(&bh->lock); + if (!list_empty(&bh->head)) + goto restart; + bh->running = false; + spin_unlock_irq(&bh->lock); +} + /** * usb_hcd_giveback_urb - return URB from HCD to device driver * @hcd: host controller returning the URB @@ -1675,25 +1728,37 @@ int usb_hcd_unlink_urb (struct urb *urb, int status) */ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) { - urb->hcpriv = NULL; - if (unlikely(urb->unlinked)) - status = urb->unlinked; - else if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) && - urb->actual_length < urb->transfer_buffer_length && - !status)) - status = -EREMOTEIO; + struct giveback_urb_bh *bh; + bool running, high_prio_bh; - unmap_urb_for_dma(hcd, urb); - usbmon_urb_complete(&hcd->self, urb, status); - usb_unanchor_urb(urb); + /* pass status to tasklet via unlinked */ + if (likely(!urb->unlinked)) + urb->unlinked = status; - /* pass ownership to the completion handler */ - urb->status = status; - urb->complete (urb); - atomic_dec (&urb->use_count); - if (unlikely(atomic_read(&urb->reject))) - wake_up (&usb_kill_urb_queue); - usb_put_urb (urb); + if (!hcd_giveback_urb_in_bh(hcd) && !is_root_hub(urb->dev)) { + __usb_hcd_giveback_urb(urb); + return; + } + + if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) { + bh = &hcd->high_prio_bh; + high_prio_bh = true; + } else { + bh = &hcd->low_prio_bh; + high_prio_bh = false; + } + + spin_lock(&bh->lock); + list_add_tail(&urb->urb_list, &bh->head); + running = bh->running; + spin_unlock(&bh->lock); + + if (running) + ; + else if (high_prio_bh) + tasklet_hi_schedule(&bh->bh); + else + tasklet_schedule(&bh->bh); } EXPORT_SYMBOL_GPL(usb_hcd_giveback_urb); @@ -2322,6 +2387,14 @@ EXPORT_SYMBOL_GPL (usb_hc_died); /*-------------------------------------------------------------------------*/ +static void init_giveback_urb_bh(struct giveback_urb_bh *bh) +{ + + spin_lock_init(&bh->lock); + INIT_LIST_HEAD(&bh->head); + tasklet_init(&bh->bh, usb_giveback_urb_bh, (unsigned long)bh); +} + /** * usb_create_shared_hcd - create and initialize an HCD structure * @driver: HC driver that will use this hcd @@ -2590,6 +2663,10 @@ int usb_add_hcd(struct usb_hcd *hcd, && device_can_wakeup(&hcd->self.root_hub->dev)) dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); + /* initialize tasklets */ + init_giveback_urb_bh(&hcd->high_prio_bh); + init_giveback_urb_bh(&hcd->low_prio_bh); + /* enable irqs just before we start the controller, * if the BIOS provides legacy PCI irqs. */ @@ -2698,6 +2775,16 @@ void usb_remove_hcd(struct usb_hcd *hcd) usb_disconnect(&rhdev); /* Sets rhdev to NULL */ mutex_unlock(&usb_bus_list_lock); + /* + * tasklet_kill() isn't needed here because: + * - driver's disconnect() called from usb_disconnect() should + * make sure its URBs are completed during the disconnect() + * callback + * + * - it is too late to run complete() here since driver may have + * been removed already now + */ + /* Prevent any more root-hub status calls from the timer. * The HCD might still restart the timer (if a port status change * interrupt occurs), but usb_hcd_poll_rh_status() won't invoke diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 1e88377e22f4..a9c7d44bd2ed 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -22,6 +22,7 @@ #ifdef __KERNEL__ #include +#include #define MAX_TOPO_LEVEL 6 @@ -67,6 +68,13 @@ /*-------------------------------------------------------------------------*/ +struct giveback_urb_bh { + bool running; + spinlock_t lock; + struct list_head head; + struct tasklet_struct bh; +}; + struct usb_hcd { /* @@ -139,6 +147,9 @@ struct usb_hcd { resource_size_t rsrc_len; /* memory/io resource length */ unsigned power_budget; /* in mA, 0 = no limit */ + struct giveback_urb_bh high_prio_bh; + struct giveback_urb_bh low_prio_bh; + /* bandwidth_mutex should be taken before adding or removing * any new bus bandwidth constraints: * 1. Before adding a configuration for a new device. @@ -221,6 +232,7 @@ struct hc_driver { #define HCD_USB25 0x0030 /* Wireless USB 1.0 (USB 2.5)*/ #define HCD_USB3 0x0040 /* USB 3.0 */ #define HCD_MASK 0x0070 +#define HCD_BH 0x0100 /* URB complete in BH context */ /* called to init HCD and root hub */ int (*reset) (struct usb_hcd *hcd); @@ -361,6 +373,11 @@ struct hc_driver { int (*find_raw_port_number)(struct usb_hcd *, int); }; +static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd) +{ + return hcd->driver->flags & HCD_BH; +} + extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); extern int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb, int status); -- cgit v1.2.3-71-gd317 From bcc48f1a7a0d40ae2e5a26aff72c2b674fd8b596 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 8 Aug 2013 21:48:22 +0800 Subject: USB: introduce usb_device_no_sg_constraint() helper Some host controllers(such as xHCI) can support building packet from discontinuous buffers, so introduce one flag and helper for this kind of host controllers, then the feature can help some applications(such as usbnet) by supporting arbitrary length of sg buffers. Acked-by: Alan Stern Signed-off-by: Ming Lei Reviewed-by: Eric Dumazet Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/urb.c | 3 ++- include/linux/usb.h | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 4e7a311ff6d7..c12bc790a6a7 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -416,7 +416,8 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) urb->iso_frame_desc[n].status = -EXDEV; urb->iso_frame_desc[n].actual_length = 0; } - } else if (dev->speed != USB_SPEED_WIRELESS && urb->num_sgs) { + } else if (urb->num_sgs && !urb->dev->bus->no_sg_constraint && + dev->speed != USB_SPEED_WIRELESS) { struct scatterlist *sg; int i; diff --git a/include/linux/usb.h b/include/linux/usb.h index acb1b0aa77d8..001629cd1a97 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -337,6 +337,7 @@ struct usb_bus { * the ep queue on a short transfer * with the URB_SHORT_NOT_OK flag set. */ + unsigned no_sg_constraint:1; /* no sg constraint */ unsigned sg_tablesize; /* 0 or largest number of sg list entries */ int devnum_next; /* Next open device number in @@ -684,6 +685,11 @@ static inline bool usb_device_supports_ltm(struct usb_device *udev) return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT; } +static inline bool usb_device_no_sg_constraint(struct usb_device *udev) +{ + return udev && udev->bus && udev->bus->no_sg_constraint; +} + /*-------------------------------------------------------------------------*/ @@ -1253,7 +1259,7 @@ typedef void (*usb_complete_t)(struct urb *); * transfer_buffer. * @sg: scatter gather buffer list, the buffer size of each element in * the list (except the last) must be divisible by the endpoint's - * max packet size + * max packet size if no_sg_constraint isn't set in 'struct usb_bus' * @num_mapped_sgs: (internal) number of mapped sg entries * @num_sgs: number of entries in the sg list * @transfer_buffer_length: How big is transfer_buffer. The transfer may -- cgit v1.2.3-71-gd317 From 638c5115a794981441246fa8fa5d95c1875af5ba Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Thu, 8 Aug 2013 21:48:24 +0800 Subject: USBNET: support DMA SG This patch introduces support of DMA SG if the USB host controller which usbnet device is attached to is capable of building packet from discontinuous buffers. The patch supports passing the skb fragment buffers to usb stack directly via urb->sg. Cc: Ben Hutchings Cc: Grant Grundler Cc: Freddy Xin Cc: Alan Stern Acked-by: Oliver Neukum Signed-off-by: Ming Lei Reviewed-by: Eric Dumazet Acked-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/usbnet.c | 45 ++++++++++++++++++++++++++++++++++++++++++--- include/linux/usb/usbnet.h | 1 + 2 files changed, 43 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 06ee82f557d4..27a00b006033 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1197,6 +1197,37 @@ EXPORT_SYMBOL_GPL(usbnet_tx_timeout); /*-------------------------------------------------------------------------*/ +static int build_dma_sg(const struct sk_buff *skb, struct urb *urb) +{ + unsigned num_sgs, total_len = 0; + int i, s = 0; + + num_sgs = skb_shinfo(skb)->nr_frags + 1; + if (num_sgs == 1) + return 0; + + urb->sg = kmalloc(num_sgs * sizeof(struct scatterlist), GFP_ATOMIC); + if (!urb->sg) + return -ENOMEM; + + urb->num_sgs = num_sgs; + sg_init_table(urb->sg, urb->num_sgs); + + sg_set_buf(&urb->sg[s++], skb->data, skb_headlen(skb)); + total_len += skb_headlen(skb); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + struct skb_frag_struct *f = &skb_shinfo(skb)->frags[i]; + + total_len += skb_frag_size(f); + sg_set_page(&urb->sg[i + s], f->page.p, f->size, + f->page_offset); + } + urb->transfer_buffer_length = total_len; + + return 1; +} + netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, struct net_device *net) { @@ -1223,7 +1254,6 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, goto drop; } } - length = skb->len; if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) { netif_dbg(dev, tx_err, dev->net, "no urb\n"); @@ -1233,10 +1263,14 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, entry = (struct skb_data *) skb->cb; entry->urb = urb; entry->dev = dev; - entry->length = length; usb_fill_bulk_urb (urb, dev->udev, dev->out, skb->data, skb->len, tx_complete, skb); + if (dev->can_dma_sg) { + if (build_dma_sg(skb, urb) < 0) + goto drop; + } + entry->length = length = urb->transfer_buffer_length; /* don't assume the hardware handles USB_ZERO_PACKET * NOTE: strictly conforming cdc-ether devices should expect @@ -1305,7 +1339,10 @@ drop: not_drop: if (skb) dev_kfree_skb_any (skb); - usb_free_urb (urb); + if (urb) { + kfree(urb->sg); + usb_free_urb(urb); + } } else netif_dbg(dev, tx_queued, dev->net, "> tx, len %d, type 0x%x\n", length, skb->protocol); @@ -1356,6 +1393,7 @@ static void usbnet_bh (unsigned long param) rx_process (dev, skb); continue; case tx_done: + kfree(entry->urb->sg); case rx_cleanup: usb_free_urb (entry->urb); dev_kfree_skb (skb); @@ -1689,6 +1727,7 @@ int usbnet_resume (struct usb_interface *intf) retval = usb_submit_urb(res, GFP_ATOMIC); if (retval < 0) { dev_kfree_skb_any(skb); + kfree(res->sg); usb_free_urb(res); usb_autopm_put_interface_async(dev->intf); } else { diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index f18d64129f99..2b47e6364e36 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -34,6 +34,7 @@ struct usbnet { struct mutex phy_mutex; unsigned char suspend_count; unsigned char pkt_cnt, pkt_err; + unsigned can_dma_sg:1; /* i/o info: pipes etc */ unsigned in, out; -- cgit v1.2.3-71-gd317 From 24d44a391f1b5d56e9c7a4fc1edd085687864ff9 Mon Sep 17 00:00:00 2001 From: Steve Wise Date: Thu, 4 Jul 2013 16:10:44 +0530 Subject: RDMA/cma: Add IPv6 support for iWARP Modify the type of local_addr and remote_addr fields in struct iw_cm_id from struct sockaddr_in to struct sockaddr_storage to hold IPv6 and IPv4 addresses uniformly. Change the references of local_addr and remote_addr in cxgb4, cxgb3, nes and amso drivers to match this. However to be able to actully run traffic over IPv6, low-level drivers have to add code to support this. Signed-off-by: Steve Wise Reviewed-by: Sean Hefty [ Fix unused variable warnings when INFINIBAND_NES_DEBUG not set. - Roland ] Signed-off-by: Roland Dreier --- drivers/infiniband/core/cma.c | 44 ++++------ drivers/infiniband/hw/amso1100/c2_ae.c | 18 ++-- drivers/infiniband/hw/amso1100/c2_cm.c | 16 +++- drivers/infiniband/hw/cxgb3/iwch_cm.c | 46 +++++++--- drivers/infiniband/hw/cxgb4/cm.c | 55 ++++++------ drivers/infiniband/hw/nes/nes_cm.c | 153 ++++++++++++++++++--------------- include/rdma/iw_cm.h | 8 +- 7 files changed, 188 insertions(+), 152 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 7c0f9535fb7d..3a2c3c3bf723 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -1385,8 +1385,9 @@ static int cma_iw_handler(struct iw_cm_id *iw_id, struct iw_cm_event *iw_event) { struct rdma_id_private *id_priv = iw_id->context; struct rdma_cm_event event; - struct sockaddr_in *sin; int ret = 0; + struct sockaddr *laddr = (struct sockaddr *)&iw_event->local_addr; + struct sockaddr *raddr = (struct sockaddr *)&iw_event->remote_addr; if (cma_disable_callback(id_priv, RDMA_CM_CONNECT)) return 0; @@ -1397,10 +1398,10 @@ static int cma_iw_handler(struct iw_cm_id *iw_id, struct iw_cm_event *iw_event) event.event = RDMA_CM_EVENT_DISCONNECTED; break; case IW_CM_EVENT_CONNECT_REPLY: - sin = (struct sockaddr_in *) cma_src_addr(id_priv); - *sin = iw_event->local_addr; - sin = (struct sockaddr_in *) cma_dst_addr(id_priv); - *sin = iw_event->remote_addr; + memcpy(cma_src_addr(id_priv), laddr, + rdma_addr_size(laddr)); + memcpy(cma_dst_addr(id_priv), raddr, + rdma_addr_size(raddr)); switch (iw_event->status) { case 0: event.event = RDMA_CM_EVENT_ESTABLISHED; @@ -1450,11 +1451,12 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id, { struct rdma_cm_id *new_cm_id; struct rdma_id_private *listen_id, *conn_id; - struct sockaddr_in *sin; struct net_device *dev = NULL; struct rdma_cm_event event; int ret; struct ib_device_attr attr; + struct sockaddr *laddr = (struct sockaddr *)&iw_event->local_addr; + struct sockaddr *raddr = (struct sockaddr *)&iw_event->remote_addr; listen_id = cm_id->context; if (cma_disable_callback(listen_id, RDMA_CM_LISTEN)) @@ -1472,14 +1474,7 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id, mutex_lock_nested(&conn_id->handler_mutex, SINGLE_DEPTH_NESTING); conn_id->state = RDMA_CM_CONNECT; - dev = ip_dev_find(&init_net, iw_event->local_addr.sin_addr.s_addr); - if (!dev) { - ret = -EADDRNOTAVAIL; - mutex_unlock(&conn_id->handler_mutex); - rdma_destroy_id(new_cm_id); - goto out; - } - ret = rdma_copy_addr(&conn_id->id.route.addr.dev_addr, dev, NULL); + ret = rdma_translate_ip(laddr, &conn_id->id.route.addr.dev_addr); if (ret) { mutex_unlock(&conn_id->handler_mutex); rdma_destroy_id(new_cm_id); @@ -1497,10 +1492,8 @@ static int iw_conn_req_handler(struct iw_cm_id *cm_id, cm_id->context = conn_id; cm_id->cm_handler = cma_iw_handler; - sin = (struct sockaddr_in *) cma_src_addr(conn_id); - *sin = iw_event->local_addr; - sin = (struct sockaddr_in *) cma_dst_addr(conn_id); - *sin = iw_event->remote_addr; + memcpy(cma_src_addr(conn_id), laddr, rdma_addr_size(laddr)); + memcpy(cma_dst_addr(conn_id), raddr, rdma_addr_size(raddr)); ret = ib_query_device(conn_id->id.device, &attr); if (ret) { @@ -1576,7 +1569,6 @@ static int cma_ib_listen(struct rdma_id_private *id_priv) static int cma_iw_listen(struct rdma_id_private *id_priv, int backlog) { int ret; - struct sockaddr_in *sin; struct iw_cm_id *id; id = iw_create_cm_id(id_priv->id.device, @@ -1587,8 +1579,8 @@ static int cma_iw_listen(struct rdma_id_private *id_priv, int backlog) id_priv->cm_id.iw = id; - sin = (struct sockaddr_in *) cma_src_addr(id_priv); - id_priv->cm_id.iw->local_addr = *sin; + memcpy(&id_priv->cm_id.iw->local_addr, cma_src_addr(id_priv), + rdma_addr_size(cma_src_addr(id_priv))); ret = iw_cm_listen(id_priv->cm_id.iw, backlog); @@ -2803,7 +2795,6 @@ static int cma_connect_iw(struct rdma_id_private *id_priv, struct rdma_conn_param *conn_param) { struct iw_cm_id *cm_id; - struct sockaddr_in* sin; int ret; struct iw_cm_conn_param iw_param; @@ -2813,11 +2804,10 @@ static int cma_connect_iw(struct rdma_id_private *id_priv, id_priv->cm_id.iw = cm_id; - sin = (struct sockaddr_in *) cma_src_addr(id_priv); - cm_id->local_addr = *sin; - - sin = (struct sockaddr_in *) cma_dst_addr(id_priv); - cm_id->remote_addr = *sin; + memcpy(&cm_id->local_addr, cma_src_addr(id_priv), + rdma_addr_size(cma_src_addr(id_priv))); + memcpy(&cm_id->remote_addr, cma_dst_addr(id_priv), + rdma_addr_size(cma_dst_addr(id_priv))); ret = cma_modify_qp_rtr(id_priv, conn_param); if (ret) diff --git a/drivers/infiniband/hw/amso1100/c2_ae.c b/drivers/infiniband/hw/amso1100/c2_ae.c index 706cf97cbe8f..d5d1929753e4 100644 --- a/drivers/infiniband/hw/amso1100/c2_ae.c +++ b/drivers/infiniband/hw/amso1100/c2_ae.c @@ -155,6 +155,8 @@ void c2_ae_event(struct c2_dev *c2dev, u32 mq_index) enum c2_event_id event_id; unsigned long flags; int status; + struct sockaddr_in *laddr = (struct sockaddr_in *)&cm_event.local_addr; + struct sockaddr_in *raddr = (struct sockaddr_in *)&cm_event.remote_addr; /* * retrieve the message @@ -206,10 +208,10 @@ void c2_ae_event(struct c2_dev *c2dev, u32 mq_index) case CCAE_ACTIVE_CONNECT_RESULTS: res = &wr->ae.ae_active_connect_results; cm_event.event = IW_CM_EVENT_CONNECT_REPLY; - cm_event.local_addr.sin_addr.s_addr = res->laddr; - cm_event.remote_addr.sin_addr.s_addr = res->raddr; - cm_event.local_addr.sin_port = res->lport; - cm_event.remote_addr.sin_port = res->rport; + laddr->sin_addr.s_addr = res->laddr; + raddr->sin_addr.s_addr = res->raddr; + laddr->sin_port = res->lport; + raddr->sin_port = res->rport; if (status == 0) { cm_event.private_data_len = be32_to_cpu(res->private_data_length); @@ -281,10 +283,10 @@ void c2_ae_event(struct c2_dev *c2dev, u32 mq_index) } cm_event.event = IW_CM_EVENT_CONNECT_REQUEST; cm_event.provider_data = (void*)(unsigned long)req->cr_handle; - cm_event.local_addr.sin_addr.s_addr = req->laddr; - cm_event.remote_addr.sin_addr.s_addr = req->raddr; - cm_event.local_addr.sin_port = req->lport; - cm_event.remote_addr.sin_port = req->rport; + laddr->sin_addr.s_addr = req->laddr; + raddr->sin_addr.s_addr = req->raddr; + laddr->sin_port = req->lport; + raddr->sin_port = req->rport; cm_event.private_data_len = be32_to_cpu(req->private_data_length); cm_event.private_data = req->private_data; diff --git a/drivers/infiniband/hw/amso1100/c2_cm.c b/drivers/infiniband/hw/amso1100/c2_cm.c index 95f58ab1e0b8..23bfa94fbd4e 100644 --- a/drivers/infiniband/hw/amso1100/c2_cm.c +++ b/drivers/infiniband/hw/amso1100/c2_cm.c @@ -46,6 +46,10 @@ int c2_llp_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) struct c2wr_qp_connect_req *wr; /* variable size needs a malloc. */ struct c2_vq_req *vq_req; int err; + struct sockaddr_in *raddr = (struct sockaddr_in *)&cm_id->remote_addr; + + if (cm_id->remote_addr.ss_family != AF_INET) + return -ENOSYS; ibqp = c2_get_qp(cm_id->device, iw_param->qpn); if (!ibqp) @@ -91,8 +95,8 @@ int c2_llp_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) wr->rnic_handle = c2dev->adapter_handle; wr->qp_handle = qp->adapter_handle; - wr->remote_addr = cm_id->remote_addr.sin_addr.s_addr; - wr->remote_port = cm_id->remote_addr.sin_port; + wr->remote_addr = raddr->sin_addr.s_addr; + wr->remote_port = raddr->sin_port; /* * Move any private data from the callers's buf into @@ -135,6 +139,10 @@ int c2_llp_service_create(struct iw_cm_id *cm_id, int backlog) struct c2wr_ep_listen_create_rep *reply; struct c2_vq_req *vq_req; int err; + struct sockaddr_in *laddr = (struct sockaddr_in *)&cm_id->local_addr; + + if (cm_id->local_addr.ss_family != AF_INET) + return -ENOSYS; c2dev = to_c2dev(cm_id->device); if (c2dev == NULL) @@ -153,8 +161,8 @@ int c2_llp_service_create(struct iw_cm_id *cm_id, int backlog) c2_wr_set_id(&wr, CCWR_EP_LISTEN_CREATE); wr.hdr.context = (u64) (unsigned long) vq_req; wr.rnic_handle = c2dev->adapter_handle; - wr.local_addr = cm_id->local_addr.sin_addr.s_addr; - wr.local_port = cm_id->local_addr.sin_port; + wr.local_addr = laddr->sin_addr.s_addr; + wr.local_port = laddr->sin_port; wr.backlog = cpu_to_be32(backlog); wr.user_context = (u64) (unsigned long) cm_id; diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index 3e094cd6a0e3..095bb046e2c8 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c @@ -721,8 +721,10 @@ static void connect_reply_upcall(struct iwch_ep *ep, int status) memset(&event, 0, sizeof(event)); event.event = IW_CM_EVENT_CONNECT_REPLY; event.status = status; - event.local_addr = ep->com.local_addr; - event.remote_addr = ep->com.remote_addr; + memcpy(&event.local_addr, &ep->com.local_addr, + sizeof(ep->com.local_addr)); + memcpy(&event.remote_addr, &ep->com.remote_addr, + sizeof(ep->com.remote_addr)); if ((status == 0) || (status == -ECONNREFUSED)) { event.private_data_len = ep->plen; @@ -747,8 +749,10 @@ static void connect_request_upcall(struct iwch_ep *ep) PDBG("%s ep %p tid %d\n", __func__, ep, ep->hwtid); memset(&event, 0, sizeof(event)); event.event = IW_CM_EVENT_CONNECT_REQUEST; - event.local_addr = ep->com.local_addr; - event.remote_addr = ep->com.remote_addr; + memcpy(&event.local_addr, &ep->com.local_addr, + sizeof(ep->com.local_addr)); + memcpy(&event.remote_addr, &ep->com.remote_addr, + sizeof(ep->com.local_addr)); event.private_data_len = ep->plen; event.private_data = ep->mpa_pkt + sizeof(struct mpa_message); event.provider_data = ep; @@ -1872,8 +1876,9 @@ err: static int is_loopback_dst(struct iw_cm_id *cm_id) { struct net_device *dev; + struct sockaddr_in *raddr = (struct sockaddr_in *)&cm_id->remote_addr; - dev = ip_dev_find(&init_net, cm_id->remote_addr.sin_addr.s_addr); + dev = ip_dev_find(&init_net, raddr->sin_addr.s_addr); if (!dev) return 0; dev_put(dev); @@ -1886,6 +1891,13 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) struct iwch_ep *ep; struct rtable *rt; int err = 0; + struct sockaddr_in *laddr = (struct sockaddr_in *)&cm_id->local_addr; + struct sockaddr_in *raddr = (struct sockaddr_in *)&cm_id->remote_addr; + + if (cm_id->remote_addr.ss_family != PF_INET) { + err = -ENOSYS; + goto out; + } if (is_loopback_dst(cm_id)) { err = -ENOSYS; @@ -1929,11 +1941,9 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) } /* find a route */ - rt = find_route(h->rdev.t3cdev_p, - cm_id->local_addr.sin_addr.s_addr, - cm_id->remote_addr.sin_addr.s_addr, - cm_id->local_addr.sin_port, - cm_id->remote_addr.sin_port, IPTOS_LOWDELAY); + rt = find_route(h->rdev.t3cdev_p, laddr->sin_addr.s_addr, + raddr->sin_addr.s_addr, laddr->sin_port, + raddr->sin_port, IPTOS_LOWDELAY); if (!rt) { printk(KERN_ERR MOD "%s - cannot find route.\n", __func__); err = -EHOSTUNREACH; @@ -1941,7 +1951,7 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) } ep->dst = &rt->dst; ep->l2t = t3_l2t_get(ep->com.tdev, ep->dst, NULL, - &cm_id->remote_addr.sin_addr.s_addr); + &raddr->sin_addr.s_addr); if (!ep->l2t) { printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); err = -ENOMEM; @@ -1950,8 +1960,10 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) state_set(&ep->com, CONNECTING); ep->tos = IPTOS_LOWDELAY; - ep->com.local_addr = cm_id->local_addr; - ep->com.remote_addr = cm_id->remote_addr; + memcpy(&ep->com.local_addr, &cm_id->local_addr, + sizeof(ep->com.local_addr)); + memcpy(&ep->com.remote_addr, &cm_id->remote_addr, + sizeof(ep->com.remote_addr)); /* send connect request to rnic */ err = send_connect(ep); @@ -1979,6 +1991,11 @@ int iwch_create_listen(struct iw_cm_id *cm_id, int backlog) might_sleep(); + if (cm_id->local_addr.ss_family != PF_INET) { + err = -ENOSYS; + goto fail1; + } + ep = alloc_ep(sizeof(*ep), GFP_KERNEL); if (!ep) { printk(KERN_ERR MOD "%s - cannot alloc ep.\n", __func__); @@ -1990,7 +2007,8 @@ int iwch_create_listen(struct iw_cm_id *cm_id, int backlog) cm_id->add_ref(cm_id); ep->com.cm_id = cm_id; ep->backlog = backlog; - ep->com.local_addr = cm_id->local_addr; + memcpy(&ep->com.local_addr, &cm_id->local_addr, + sizeof(ep->com.local_addr)); /* * Allocate a server TID. diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 65c30ea8c1a1..76d8812f9e9a 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -952,8 +952,10 @@ static void connect_reply_upcall(struct c4iw_ep *ep, int status) memset(&event, 0, sizeof(event)); event.event = IW_CM_EVENT_CONNECT_REPLY; event.status = status; - event.local_addr = ep->com.local_addr; - event.remote_addr = ep->com.remote_addr; + memcpy(&event.local_addr, &ep->com.local_addr, + sizeof(ep->com.local_addr)); + memcpy(&event.remote_addr, &ep->com.remote_addr, + sizeof(ep->com.remote_addr)); if ((status == 0) || (status == -ECONNREFUSED)) { if (!ep->tried_with_mpa_v1) { @@ -989,8 +991,10 @@ static void connect_request_upcall(struct c4iw_ep *ep) PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid); memset(&event, 0, sizeof(event)); event.event = IW_CM_EVENT_CONNECT_REQUEST; - event.local_addr = ep->com.local_addr; - event.remote_addr = ep->com.remote_addr; + memcpy(&event.local_addr, &ep->com.local_addr, + sizeof(ep->com.local_addr)); + memcpy(&event.remote_addr, &ep->com.remote_addr, + sizeof(ep->com.remote_addr)); event.provider_data = ep; if (!ep->tried_with_mpa_v1) { /* this means MPA_v2 is used */ @@ -1568,6 +1572,10 @@ static int c4iw_reconnect(struct c4iw_ep *ep) struct net_device *pdev; int step; struct neighbour *neigh; + struct sockaddr_in *laddr = (struct sockaddr_in *) + &ep->com.cm_id->local_addr; + struct sockaddr_in *raddr = (struct sockaddr_in *) + &ep->com.cm_id->remote_addr; PDBG("%s qp %p cm_id %p\n", __func__, ep->com.qp, ep->com.cm_id); init_timer(&ep->timer); @@ -1585,10 +1593,8 @@ static int c4iw_reconnect(struct c4iw_ep *ep) /* find a route */ rt = find_route(ep->com.dev, - ep->com.cm_id->local_addr.sin_addr.s_addr, - ep->com.cm_id->remote_addr.sin_addr.s_addr, - ep->com.cm_id->local_addr.sin_port, - ep->com.cm_id->remote_addr.sin_port, 0); + laddr->sin_addr.s_addr, raddr->sin_addr.s_addr, + laddr->sin_port, raddr->sin_port, 0); if (!rt) { pr_err("%s - cannot find route.\n", __func__); err = -EHOSTUNREACH; @@ -1596,8 +1602,7 @@ static int c4iw_reconnect(struct c4iw_ep *ep) } ep->dst = &rt->dst; - neigh = dst_neigh_lookup(ep->dst, - &ep->com.cm_id->remote_addr.sin_addr.s_addr); + neigh = dst_neigh_lookup(ep->dst, &raddr->sin_addr.s_addr); if (!neigh) { pr_err("%s - cannot alloc neigh.\n", __func__); err = -ENOMEM; @@ -1607,8 +1612,7 @@ static int c4iw_reconnect(struct c4iw_ep *ep) /* get a l2t entry */ if (neigh->dev->flags & IFF_LOOPBACK) { PDBG("%s LOOPBACK\n", __func__); - pdev = ip_dev_find(&init_net, - ep->com.cm_id->remote_addr.sin_addr.s_addr); + pdev = ip_dev_find(&init_net, raddr->sin_addr.s_addr); ep->l2t = cxgb4_l2t_get(ep->com.dev->rdev.lldi.l2t, neigh, pdev, 0); pi = (struct port_info *)netdev_priv(pdev); @@ -2518,6 +2522,8 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) struct c4iw_ep *ep; struct rtable *rt; int err = 0; + struct sockaddr_in *laddr = (struct sockaddr_in *)&cm_id->local_addr; + struct sockaddr_in *raddr = (struct sockaddr_in *)&cm_id->remote_addr; if ((conn_param->ord > c4iw_max_read_depth) || (conn_param->ird > c4iw_max_read_depth)) { @@ -2562,17 +2568,12 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) insert_handle(dev, &dev->atid_idr, ep, ep->atid); PDBG("%s saddr 0x%x sport 0x%x raddr 0x%x rport 0x%x\n", __func__, - ntohl(cm_id->local_addr.sin_addr.s_addr), - ntohs(cm_id->local_addr.sin_port), - ntohl(cm_id->remote_addr.sin_addr.s_addr), - ntohs(cm_id->remote_addr.sin_port)); + ntohl(laddr->sin_addr.s_addr), ntohs(laddr->sin_port), + ntohl(raddr->sin_addr.s_addr), ntohs(raddr->sin_port)); /* find a route */ - rt = find_route(dev, - cm_id->local_addr.sin_addr.s_addr, - cm_id->remote_addr.sin_addr.s_addr, - cm_id->local_addr.sin_port, - cm_id->remote_addr.sin_port, 0); + rt = find_route(dev, laddr->sin_addr.s_addr, raddr->sin_addr.s_addr, + laddr->sin_port, raddr->sin_port, 0); if (!rt) { printk(KERN_ERR MOD "%s - cannot find route.\n", __func__); err = -EHOSTUNREACH; @@ -2580,8 +2581,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) } ep->dst = &rt->dst; - err = import_ep(ep, cm_id->remote_addr.sin_addr.s_addr, - ep->dst, ep->com.dev, true); + err = import_ep(ep, raddr->sin_addr.s_addr, ep->dst, ep->com.dev, true); if (err) { printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); goto fail4; @@ -2593,8 +2593,10 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) state_set(&ep->com, CONNECTING); ep->tos = 0; - ep->com.local_addr = cm_id->local_addr; - ep->com.remote_addr = cm_id->remote_addr; + memcpy(&ep->com.local_addr, &cm_id->local_addr, + sizeof(ep->com.local_addr)); + memcpy(&ep->com.remote_addr, &cm_id->remote_addr, + sizeof(ep->com.remote_addr)); /* send connect request to rnic */ err = send_connect(ep); @@ -2633,7 +2635,8 @@ int c4iw_create_listen(struct iw_cm_id *cm_id, int backlog) ep->com.cm_id = cm_id; ep->com.dev = dev; ep->backlog = backlog; - ep->com.local_addr = cm_id->local_addr; + memcpy(&ep->com.local_addr, &cm_id->local_addr, + sizeof(ep->com.local_addr)); /* * Allocate a server TID. diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index 24b9f1a0107b..6b29249aa85a 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -2998,6 +2998,8 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) u8 *start_ptr = &start_addr; u8 **start_buff = &start_ptr; u16 buff_len = 0; + struct sockaddr_in *laddr = (struct sockaddr_in *)&cm_id->local_addr; + struct sockaddr_in *raddr = (struct sockaddr_in *)&cm_id->remote_addr; ibqp = nes_get_qp(cm_id->device, conn_param->qpn); if (!ibqp) @@ -3062,8 +3064,7 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) /* setup our first outgoing iWarp send WQE (the IETF frame response) */ wqe = &nesqp->hwqp.sq_vbase[0]; - if (cm_id->remote_addr.sin_addr.s_addr != - cm_id->local_addr.sin_addr.s_addr) { + if (raddr->sin_addr.s_addr != laddr->sin_addr.s_addr) { u64temp = (unsigned long)nesqp; nesibdev = nesvnic->nesibdev; nespd = nesqp->nespd; @@ -3132,13 +3133,10 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) nes_cm_init_tsa_conn(nesqp, cm_node); - nesqp->nesqp_context->tcpPorts[0] = - cpu_to_le16(ntohs(cm_id->local_addr.sin_port)); - nesqp->nesqp_context->tcpPorts[1] = - cpu_to_le16(ntohs(cm_id->remote_addr.sin_port)); + nesqp->nesqp_context->tcpPorts[0] = cpu_to_le16(ntohs(laddr->sin_port)); + nesqp->nesqp_context->tcpPorts[1] = cpu_to_le16(ntohs(raddr->sin_port)); - nesqp->nesqp_context->ip0 = - cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr)); + nesqp->nesqp_context->ip0 = cpu_to_le32(ntohl(raddr->sin_addr.s_addr)); nesqp->nesqp_context->misc2 |= cpu_to_le32( (u32)PCI_FUNC(nesdev->pcidev->devfn) << @@ -3162,9 +3160,9 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) memset(&nes_quad, 0, sizeof(nes_quad)); nes_quad.DstIpAdrIndex = cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24); - nes_quad.SrcIpadr = cm_id->remote_addr.sin_addr.s_addr; - nes_quad.TcpPorts[0] = cm_id->remote_addr.sin_port; - nes_quad.TcpPorts[1] = cm_id->local_addr.sin_port; + nes_quad.SrcIpadr = raddr->sin_addr.s_addr; + nes_quad.TcpPorts[0] = raddr->sin_port; + nes_quad.TcpPorts[1] = laddr->sin_port; /* Produce hash key */ crc_value = get_crc_value(&nes_quad); @@ -3180,10 +3178,8 @@ int nes_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) nes_debug(NES_DBG_CM, "QP%u, Destination IP = 0x%08X:0x%04X, local = " "0x%08X:0x%04X, rcv_nxt=0x%08X, snd_nxt=0x%08X, mpa + " "private data length=%u.\n", nesqp->hwqp.qp_id, - ntohl(cm_id->remote_addr.sin_addr.s_addr), - ntohs(cm_id->remote_addr.sin_port), - ntohl(cm_id->local_addr.sin_addr.s_addr), - ntohs(cm_id->local_addr.sin_port), + ntohl(raddr->sin_addr.s_addr), ntohs(raddr->sin_port), + ntohl(laddr->sin_addr.s_addr), ntohs(laddr->sin_port), le32_to_cpu(nesqp->nesqp_context->rcv_nxt), le32_to_cpu(nesqp->nesqp_context->snd_nxt), buff_len); @@ -3263,7 +3259,11 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) struct nes_cm_node *cm_node; struct nes_cm_info cm_info; int apbvt_set = 0; + struct sockaddr_in *laddr = (struct sockaddr_in *)&cm_id->local_addr; + struct sockaddr_in *raddr = (struct sockaddr_in *)&cm_id->remote_addr; + if (cm_id->remote_addr.ss_family != AF_INET) + return -ENOSYS; ibqp = nes_get_qp(cm_id->device, conn_param->qpn); if (!ibqp) return -EINVAL; @@ -3277,16 +3277,14 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) if (!nesdev) return -EINVAL; - if (!(cm_id->local_addr.sin_port) || !(cm_id->remote_addr.sin_port)) + if (!laddr->sin_port || !raddr->sin_port) return -EINVAL; nes_debug(NES_DBG_CM, "QP%u, current IP = 0x%08X, Destination IP = " "0x%08X:0x%04X, local = 0x%08X:0x%04X.\n", nesqp->hwqp.qp_id, - ntohl(nesvnic->local_ipaddr), - ntohl(cm_id->remote_addr.sin_addr.s_addr), - ntohs(cm_id->remote_addr.sin_port), - ntohl(cm_id->local_addr.sin_addr.s_addr), - ntohs(cm_id->local_addr.sin_port)); + ntohl(nesvnic->local_ipaddr), ntohl(raddr->sin_addr.s_addr), + ntohs(raddr->sin_port), ntohl(laddr->sin_addr.s_addr), + ntohs(laddr->sin_port)); atomic_inc(&cm_connects); nesqp->active_conn = 1; @@ -3306,18 +3304,18 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) nes_debug(NES_DBG_CM, "mpa private data len =%u\n", conn_param->private_data_len); - if (cm_id->local_addr.sin_addr.s_addr != - cm_id->remote_addr.sin_addr.s_addr) { - nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port), - PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD); + if (laddr->sin_addr.s_addr != raddr->sin_addr.s_addr) { + nes_manage_apbvt(nesvnic, ntohs(laddr->sin_port), + PCI_FUNC(nesdev->pcidev->devfn), + NES_MANAGE_APBVT_ADD); apbvt_set = 1; } /* set up the connection params for the node */ - cm_info.loc_addr = htonl(cm_id->local_addr.sin_addr.s_addr); - cm_info.loc_port = htons(cm_id->local_addr.sin_port); - cm_info.rem_addr = htonl(cm_id->remote_addr.sin_addr.s_addr); - cm_info.rem_port = htons(cm_id->remote_addr.sin_port); + cm_info.loc_addr = htonl(laddr->sin_addr.s_addr); + cm_info.loc_port = htons(laddr->sin_port); + cm_info.rem_addr = htonl(raddr->sin_addr.s_addr); + cm_info.rem_port = htons(raddr->sin_port); cm_info.cm_id = cm_id; cm_info.conn_type = NES_CM_IWARP_CONN_TYPE; @@ -3329,7 +3327,7 @@ int nes_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) &cm_info); if (!cm_node) { if (apbvt_set) - nes_manage_apbvt(nesvnic, ntohs(cm_id->local_addr.sin_port), + nes_manage_apbvt(nesvnic, ntohs(laddr->sin_port), PCI_FUNC(nesdev->pcidev->devfn), NES_MANAGE_APBVT_DEL); @@ -3355,10 +3353,13 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog) struct nes_cm_listener *cm_node; struct nes_cm_info cm_info; int err; + struct sockaddr_in *laddr = (struct sockaddr_in *)&cm_id->local_addr; nes_debug(NES_DBG_CM, "cm_id = %p, local port = 0x%04X.\n", - cm_id, ntohs(cm_id->local_addr.sin_port)); + cm_id, ntohs(laddr->sin_port)); + if (cm_id->local_addr.ss_family != AF_INET) + return -ENOSYS; nesvnic = to_nesvnic(cm_id->device); if (!nesvnic) return -EINVAL; @@ -3367,11 +3368,11 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog) nesvnic, nesvnic->netdev, nesvnic->netdev->name); nes_debug(NES_DBG_CM, "nesvnic->local_ipaddr=0x%08x, sin_addr.s_addr=0x%08x\n", - nesvnic->local_ipaddr, cm_id->local_addr.sin_addr.s_addr); + nesvnic->local_ipaddr, laddr->sin_addr.s_addr); /* setup listen params in our api call struct */ cm_info.loc_addr = nesvnic->local_ipaddr; - cm_info.loc_port = cm_id->local_addr.sin_port; + cm_info.loc_port = laddr->sin_port; cm_info.backlog = backlog; cm_info.cm_id = cm_id; @@ -3388,8 +3389,7 @@ int nes_create_listen(struct iw_cm_id *cm_id, int backlog) cm_id->provider_data = cm_node; if (!cm_node->reused_node) { - err = nes_manage_apbvt(nesvnic, - ntohs(cm_id->local_addr.sin_port), + err = nes_manage_apbvt(nesvnic, ntohs(laddr->sin_port), PCI_FUNC(nesvnic->nesdev->pcidev->devfn), NES_MANAGE_APBVT_ADD); if (err) { @@ -3487,6 +3487,9 @@ static void cm_event_connected(struct nes_cm_event *event) struct nes_v4_quad nes_quad; u32 crc_value; int ret; + struct sockaddr_in *laddr; + struct sockaddr_in *raddr; + struct sockaddr_in *cm_event_laddr; /* get all our handles */ cm_node = event->cm_node; @@ -3496,27 +3499,24 @@ static void cm_event_connected(struct nes_cm_event *event) nesvnic = to_nesvnic(nesqp->ibqp.device); nesdev = nesvnic->nesdev; nesadapter = nesdev->nesadapter; + laddr = (struct sockaddr_in *)&cm_id->local_addr; + raddr = (struct sockaddr_in *)&cm_id->remote_addr; + cm_event_laddr = (struct sockaddr_in *)&cm_event.local_addr; if (nesqp->destroyed) return; atomic_inc(&cm_connecteds); nes_debug(NES_DBG_CM, "QP%u attempting to connect to 0x%08X:0x%04X on" " local port 0x%04X. jiffies = %lu.\n", - nesqp->hwqp.qp_id, - ntohl(cm_id->remote_addr.sin_addr.s_addr), - ntohs(cm_id->remote_addr.sin_port), - ntohs(cm_id->local_addr.sin_port), - jiffies); + nesqp->hwqp.qp_id, ntohl(raddr->sin_addr.s_addr), + ntohs(raddr->sin_port), ntohs(laddr->sin_port), jiffies); nes_cm_init_tsa_conn(nesqp, cm_node); /* set the QP tsa context */ - nesqp->nesqp_context->tcpPorts[0] = - cpu_to_le16(ntohs(cm_id->local_addr.sin_port)); - nesqp->nesqp_context->tcpPorts[1] = - cpu_to_le16(ntohs(cm_id->remote_addr.sin_port)); - nesqp->nesqp_context->ip0 = - cpu_to_le32(ntohl(cm_id->remote_addr.sin_addr.s_addr)); + nesqp->nesqp_context->tcpPorts[0] = cpu_to_le16(ntohs(laddr->sin_port)); + nesqp->nesqp_context->tcpPorts[1] = cpu_to_le16(ntohs(raddr->sin_port)); + nesqp->nesqp_context->ip0 = cpu_to_le32(ntohl(raddr->sin_addr.s_addr)); nesqp->nesqp_context->misc2 |= cpu_to_le32( (u32)PCI_FUNC(nesdev->pcidev->devfn) << @@ -3544,9 +3544,9 @@ static void cm_event_connected(struct nes_cm_event *event) nes_quad.DstIpAdrIndex = cpu_to_le32((u32)PCI_FUNC(nesdev->pcidev->devfn) << 24); - nes_quad.SrcIpadr = cm_id->remote_addr.sin_addr.s_addr; - nes_quad.TcpPorts[0] = cm_id->remote_addr.sin_port; - nes_quad.TcpPorts[1] = cm_id->local_addr.sin_port; + nes_quad.SrcIpadr = raddr->sin_addr.s_addr; + nes_quad.TcpPorts[0] = raddr->sin_port; + nes_quad.TcpPorts[1] = laddr->sin_port; /* Produce hash key */ crc_value = get_crc_value(&nes_quad); @@ -3565,8 +3565,8 @@ static void cm_event_connected(struct nes_cm_event *event) cm_event.event = IW_CM_EVENT_CONNECT_REPLY; cm_event.status = 0; cm_event.provider_data = cm_id->provider_data; - cm_event.local_addr.sin_family = AF_INET; - cm_event.local_addr.sin_port = cm_id->local_addr.sin_port; + cm_event_laddr->sin_family = AF_INET; + cm_event_laddr->sin_port = laddr->sin_port; cm_event.remote_addr = cm_id->remote_addr; cm_event.private_data = (void *)event->cm_node->mpa_frame_buf; @@ -3574,7 +3574,7 @@ static void cm_event_connected(struct nes_cm_event *event) cm_event.ird = cm_node->ird_size; cm_event.ord = cm_node->ord_size; - cm_event.local_addr.sin_addr.s_addr = event->cm_info.rem_addr; + cm_event_laddr->sin_addr.s_addr = event->cm_info.rem_addr; ret = cm_id->event_handler(cm_id, &cm_event); nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret); @@ -3627,9 +3627,16 @@ static void cm_event_connect_error(struct nes_cm_event *event) cm_event.private_data = NULL; cm_event.private_data_len = 0; - nes_debug(NES_DBG_CM, "call CM_EVENT REJECTED, local_addr=%08x, " - "remove_addr=%08x\n", cm_event.local_addr.sin_addr.s_addr, - cm_event.remote_addr.sin_addr.s_addr); +#ifdef CONFIG_INFINIBAND_NES_DEBUG + { + struct sockaddr_in *cm_event_laddr = (struct sockaddr_in *) + &cm_event.local_addr; + struct sockaddr_in *cm_event_raddr = (struct sockaddr_in *) + &cm_event.remote_addr; + nes_debug(NES_DBG_CM, "call CM_EVENT REJECTED, local_addr=%08x, remote_addr=%08x\n", + cm_event_laddr->sin_addr.s_addr, cm_event_raddr->sin_addr.s_addr); + } +#endif ret = cm_id->event_handler(cm_id, &cm_event); nes_debug(NES_DBG_CM, "OFA CM event_handler returned, ret=%d\n", ret); @@ -3709,6 +3716,10 @@ static void cm_event_mpa_req(struct nes_cm_event *event) struct iw_cm_event cm_event; int ret; struct nes_cm_node *cm_node; + struct sockaddr_in *cm_event_laddr = (struct sockaddr_in *) + &cm_event.local_addr; + struct sockaddr_in *cm_event_raddr = (struct sockaddr_in *) + &cm_event.remote_addr; cm_node = event->cm_node; if (!cm_node) @@ -3723,13 +3734,13 @@ static void cm_event_mpa_req(struct nes_cm_event *event) cm_event.status = 0; cm_event.provider_data = (void *)cm_node; - cm_event.local_addr.sin_family = AF_INET; - cm_event.local_addr.sin_port = htons(event->cm_info.loc_port); - cm_event.local_addr.sin_addr.s_addr = htonl(event->cm_info.loc_addr); + cm_event_laddr->sin_family = AF_INET; + cm_event_laddr->sin_port = htons(event->cm_info.loc_port); + cm_event_laddr->sin_addr.s_addr = htonl(event->cm_info.loc_addr); - cm_event.remote_addr.sin_family = AF_INET; - cm_event.remote_addr.sin_port = htons(event->cm_info.rem_port); - cm_event.remote_addr.sin_addr.s_addr = htonl(event->cm_info.rem_addr); + cm_event_raddr->sin_family = AF_INET; + cm_event_raddr->sin_port = htons(event->cm_info.rem_port); + cm_event_raddr->sin_addr.s_addr = htonl(event->cm_info.rem_addr); cm_event.private_data = cm_node->mpa_frame_buf; cm_event.private_data_len = (u8)cm_node->mpa_frame_size; cm_event.ird = cm_node->ird_size; @@ -3749,6 +3760,10 @@ static void cm_event_mpa_reject(struct nes_cm_event *event) struct iw_cm_event cm_event; struct nes_cm_node *cm_node; int ret; + struct sockaddr_in *cm_event_laddr = (struct sockaddr_in *) + &cm_event.local_addr; + struct sockaddr_in *cm_event_raddr = (struct sockaddr_in *) + &cm_event.remote_addr; cm_node = event->cm_node; if (!cm_node) @@ -3763,21 +3778,21 @@ static void cm_event_mpa_reject(struct nes_cm_event *event) cm_event.status = -ECONNREFUSED; cm_event.provider_data = cm_id->provider_data; - cm_event.local_addr.sin_family = AF_INET; - cm_event.local_addr.sin_port = htons(event->cm_info.loc_port); - cm_event.local_addr.sin_addr.s_addr = htonl(event->cm_info.loc_addr); + cm_event_laddr->sin_family = AF_INET; + cm_event_laddr->sin_port = htons(event->cm_info.loc_port); + cm_event_laddr->sin_addr.s_addr = htonl(event->cm_info.loc_addr); - cm_event.remote_addr.sin_family = AF_INET; - cm_event.remote_addr.sin_port = htons(event->cm_info.rem_port); - cm_event.remote_addr.sin_addr.s_addr = htonl(event->cm_info.rem_addr); + cm_event_raddr->sin_family = AF_INET; + cm_event_raddr->sin_port = htons(event->cm_info.rem_port); + cm_event_raddr->sin_addr.s_addr = htonl(event->cm_info.rem_addr); cm_event.private_data = cm_node->mpa_frame_buf; cm_event.private_data_len = (u8)cm_node->mpa_frame_size; nes_debug(NES_DBG_CM, "call CM_EVENT_MPA_REJECTED, local_addr=%08x, " "remove_addr=%08x\n", - cm_event.local_addr.sin_addr.s_addr, - cm_event.remote_addr.sin_addr.s_addr); + cm_event_laddr->sin_addr.s_addr, + cm_event_raddr->sin_addr.s_addr); ret = cm_id->event_handler(cm_id, &cm_event); if (ret) diff --git a/include/rdma/iw_cm.h b/include/rdma/iw_cm.h index 1a046b1595cc..1017e0bdf8ba 100644 --- a/include/rdma/iw_cm.h +++ b/include/rdma/iw_cm.h @@ -49,8 +49,8 @@ enum iw_cm_event_type { struct iw_cm_event { enum iw_cm_event_type event; int status; - struct sockaddr_in local_addr; - struct sockaddr_in remote_addr; + struct sockaddr_storage local_addr; + struct sockaddr_storage remote_addr; void *private_data; void *provider_data; u8 private_data_len; @@ -83,8 +83,8 @@ struct iw_cm_id { iw_cm_handler cm_handler; /* client callback function */ void *context; /* client cb context */ struct ib_device *device; - struct sockaddr_in local_addr; - struct sockaddr_in remote_addr; + struct sockaddr_storage local_addr; + struct sockaddr_storage remote_addr; void *provider_data; /* provider private data */ iw_event_handler event_handler; /* cb for provider events */ -- cgit v1.2.3-71-gd317 From 3775a209d38aa3a0c7ed89a7d0f529e0230f280e Mon Sep 17 00:00:00 2001 From: Casey Leedom Date: Tue, 6 Aug 2013 15:48:36 +0530 Subject: PCI: Add pci_wait_for_pending_transaction() New routine to avoid duplication of code to wait for pending PCI transactions to complete. Signed-off-by: Casey Leedom Signed-off-by: Vipul Pandya Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 38 +++++++++++++++++++++++++------------- include/linux/pci.h | 1 + 2 files changed, 26 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e37fea6e178d..10ab64e8878e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3098,19 +3098,17 @@ int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask) } EXPORT_SYMBOL(pci_set_dma_seg_boundary); -static int pcie_flr(struct pci_dev *dev, int probe) +/** + * pci_wait_for_pending_transaction - waits for pending transaction + * @dev: the PCI device to operate on + * + * Return 0 if transaction is pending 1 otherwise. + */ +int pci_wait_for_pending_transaction(struct pci_dev *dev) { int i; - u32 cap; u16 status; - pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap); - if (!(cap & PCI_EXP_DEVCAP_FLR)) - return -ENOTTY; - - if (probe) - return 0; - /* Wait for Transaction Pending bit clean */ for (i = 0; i < 4; i++) { if (i) @@ -3118,13 +3116,27 @@ static int pcie_flr(struct pci_dev *dev, int probe) pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status); if (!(status & PCI_EXP_DEVSTA_TRPND)) - goto clear; + return 1; } - dev_err(&dev->dev, "transaction is not cleared; " - "proceeding with reset anyway\n"); + return 0; +} +EXPORT_SYMBOL(pci_wait_for_pending_transaction); + +static int pcie_flr(struct pci_dev *dev, int probe) +{ + u32 cap; + + pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap); + if (!(cap & PCI_EXP_DEVCAP_FLR)) + return -ENOTTY; + + if (probe) + return 0; + + if (!pci_wait_for_pending_transaction(dev)) + dev_err(&dev->dev, "transaction is not cleared; proceeding with reset anyway\n"); -clear: pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR); msleep(100); diff --git a/include/linux/pci.h b/include/linux/pci.h index 0fd1f1582fa1..e6470016c718 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -914,6 +914,7 @@ bool pci_check_and_unmask_intx(struct pci_dev *dev); void pci_msi_off(struct pci_dev *dev); int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size); int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask); +int pci_wait_for_pending_transaction(struct pci_dev *dev); int pcix_get_max_mmrbc(struct pci_dev *dev); int pcix_get_mmrbc(struct pci_dev *dev); int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc); -- cgit v1.2.3-71-gd317 From 11ad39ede24ee42909d58dc95031d96da46e33bd Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Sat, 6 Apr 2013 02:40:36 +0200 Subject: pwm: Add new pwm-samsung driver This patch introduces new Samsung PWM driver, which is completely rewritten to be multiplatform- and DeviceTree-aware. In addition, remaining problems of old driver are fixed, such as: - proper handling of hardware variants, - synchronization on SMP systems, - handling of boundary parameter values, - hardware sharing with PWM clocksource driver, - undefined state of PWM output after stopping PWM channel. Signed-off-by: Tomasz Figa Reviewed-by: Sylwester Nawrocki Tested-by: Heiko Stuebner Tested-by: Mark Brown Tested-by: Sylwester Nawrocki Acked-by: Arnd Bergmann Acked-by: Thierry Reding --- drivers/pwm/Makefile | 1 + drivers/pwm/pwm-samsung.c | 618 ++++++++++++++++++++++++++++++++++++++ include/clocksource/samsung_pwm.h | 7 + 3 files changed, 626 insertions(+) create mode 100644 drivers/pwm/pwm-samsung.c (limited to 'include') diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index aa94807b7d61..86a57713b721 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o obj-$(CONFIG_PWM_PXA) += pwm-pxa.o obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung-legacy.o +obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c new file mode 100644 index 000000000000..fcc8b9adde9f --- /dev/null +++ b/drivers/pwm/pwm-samsung.c @@ -0,0 +1,618 @@ +/* + * Copyright (c) 2007 Ben Dooks + * Copyright (c) 2008 Simtec Electronics + * Ben Dooks , + * Copyright (c) 2013 Tomasz Figa + * + * PWM driver for Samsung SoCs + * + * 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; either version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* For struct samsung_timer_variant and samsung_pwm_lock. */ +#include + +#define REG_TCFG0 0x00 +#define REG_TCFG1 0x04 +#define REG_TCON 0x08 + +#define REG_TCNTB(chan) (0x0c + ((chan) * 0xc)) +#define REG_TCMPB(chan) (0x10 + ((chan) * 0xc)) + +#define TCFG0_PRESCALER_MASK 0xff +#define TCFG0_PRESCALER1_SHIFT 8 + +#define TCFG1_MUX_MASK 0xf +#define TCFG1_SHIFT(chan) (4 * (chan)) + +/* + * Each channel occupies 4 bits in TCON register, but there is a gap of 4 + * bits (one channel) after channel 0, so channels have different numbering + * when accessing TCON register. See to_tcon_channel() function. + * + * In addition, the location of autoreload bit for channel 4 (TCON channel 5) + * in its set of bits is 2 as opposed to 3 for other channels. + */ +#define TCON_START(chan) BIT(4 * (chan) + 0) +#define TCON_MANUALUPDATE(chan) BIT(4 * (chan) + 1) +#define TCON_INVERT(chan) BIT(4 * (chan) + 2) +#define _TCON_AUTORELOAD(chan) BIT(4 * (chan) + 3) +#define _TCON_AUTORELOAD4(chan) BIT(4 * (chan) + 2) +#define TCON_AUTORELOAD(chan) \ + ((chan < 5) ? _TCON_AUTORELOAD(chan) : _TCON_AUTORELOAD4(chan)) + +/** + * struct samsung_pwm_channel - private data of PWM channel + * @period_ns: current period in nanoseconds programmed to the hardware + * @duty_ns: current duty time in nanoseconds programmed to the hardware + * @tin_ns: time of one timer tick in nanoseconds with current timer rate + */ +struct samsung_pwm_channel { + u32 period_ns; + u32 duty_ns; + u32 tin_ns; +}; + +/** + * struct samsung_pwm_chip - private data of PWM chip + * @chip: generic PWM chip + * @variant: local copy of hardware variant data + * @inverter_mask: inverter status for all channels - one bit per channel + * @base: base address of mapped PWM registers + * @base_clk: base clock used to drive the timers + * @tclk0: external clock 0 (can be ERR_PTR if not present) + * @tclk1: external clock 1 (can be ERR_PTR if not present) + */ +struct samsung_pwm_chip { + struct pwm_chip chip; + struct samsung_pwm_variant variant; + u8 inverter_mask; + + void __iomem *base; + struct clk *base_clk; + struct clk *tclk0; + struct clk *tclk1; +}; + +#ifndef CONFIG_CLKSRC_SAMSUNG_PWM +/* + * PWM block is shared between pwm-samsung and samsung_pwm_timer drivers + * and some registers need access synchronization. If both drivers are + * compiled in, the spinlock is defined in the clocksource driver, + * otherwise following definition is used. + * + * Currently we do not need any more complex synchronization method + * because all the supported SoCs contain only one instance of the PWM + * IP. Should this change, both drivers will need to be modified to + * properly synchronize accesses to particular instances. + */ +static DEFINE_SPINLOCK(samsung_pwm_lock); +#endif + +static inline +struct samsung_pwm_chip *to_samsung_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct samsung_pwm_chip, chip); +} + +static inline unsigned int to_tcon_channel(unsigned int channel) +{ + /* TCON register has a gap of 4 bits (1 channel) after channel 0 */ + return (channel == 0) ? 0 : (channel + 1); +} + +static void pwm_samsung_set_divisor(struct samsung_pwm_chip *pwm, + unsigned int channel, u8 divisor) +{ + u8 shift = TCFG1_SHIFT(channel); + unsigned long flags; + u32 reg; + u8 bits; + + bits = (fls(divisor) - 1) - pwm->variant.div_base; + + spin_lock_irqsave(&samsung_pwm_lock, flags); + + reg = readl(pwm->base + REG_TCFG1); + reg &= ~(TCFG1_MUX_MASK << shift); + reg |= bits << shift; + writel(reg, pwm->base + REG_TCFG1); + + spin_unlock_irqrestore(&samsung_pwm_lock, flags); +} + +static int pwm_samsung_is_tdiv(struct samsung_pwm_chip *chip, unsigned int chan) +{ + struct samsung_pwm_variant *variant = &chip->variant; + u32 reg; + + reg = readl(chip->base + REG_TCFG1); + reg >>= TCFG1_SHIFT(chan); + reg &= TCFG1_MUX_MASK; + + return (BIT(reg) & variant->tclk_mask) == 0; +} + +static unsigned long pwm_samsung_get_tin_rate(struct samsung_pwm_chip *chip, + unsigned int chan) +{ + unsigned long rate; + u32 reg; + + rate = clk_get_rate(chip->base_clk); + + reg = readl(chip->base + REG_TCFG0); + if (chan >= 2) + reg >>= TCFG0_PRESCALER1_SHIFT; + reg &= TCFG0_PRESCALER_MASK; + + return rate / (reg + 1); +} + +static unsigned long pwm_samsung_calc_tin(struct samsung_pwm_chip *chip, + unsigned int chan, unsigned long freq) +{ + struct samsung_pwm_variant *variant = &chip->variant; + unsigned long rate; + struct clk *clk; + u8 div; + + if (!pwm_samsung_is_tdiv(chip, chan)) { + clk = (chan < 2) ? chip->tclk0 : chip->tclk1; + if (!IS_ERR(clk)) { + rate = clk_get_rate(clk); + if (rate) + return rate; + } + + dev_warn(chip->chip.dev, + "tclk of PWM %d is inoperational, using tdiv\n", chan); + } + + rate = pwm_samsung_get_tin_rate(chip, chan); + dev_dbg(chip->chip.dev, "tin parent at %lu\n", rate); + + /* + * Compare minimum PWM frequency that can be achieved with possible + * divider settings and choose the lowest divisor that can generate + * frequencies lower than requested. + */ + for (div = variant->div_base; div < 4; ++div) + if ((rate >> (variant->bits + div)) < freq) + break; + + pwm_samsung_set_divisor(chip, chan, BIT(div)); + + return rate >> div; +} + +static int pwm_samsung_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); + struct samsung_pwm_channel *our_chan; + + if (!(our_chip->variant.output_mask & BIT(pwm->hwpwm))) { + dev_warn(chip->dev, + "tried to request PWM channel %d without output\n", + pwm->hwpwm); + return -EINVAL; + } + + our_chan = devm_kzalloc(chip->dev, sizeof(*our_chan), GFP_KERNEL); + if (!our_chan) + return -ENOMEM; + + pwm_set_chip_data(pwm, our_chan); + + return 0; +} + +static void pwm_samsung_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + pwm_set_chip_data(pwm, NULL); + devm_kfree(chip->dev, pwm_get_chip_data(pwm)); +} + +static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); + unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm); + unsigned long flags; + u32 tcon; + + spin_lock_irqsave(&samsung_pwm_lock, flags); + + tcon = readl(our_chip->base + REG_TCON); + + tcon &= ~TCON_START(tcon_chan); + tcon |= TCON_MANUALUPDATE(tcon_chan); + writel(tcon, our_chip->base + REG_TCON); + + tcon &= ~TCON_MANUALUPDATE(tcon_chan); + tcon |= TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan); + writel(tcon, our_chip->base + REG_TCON); + + spin_unlock_irqrestore(&samsung_pwm_lock, flags); + + return 0; +} + +static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); + unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm); + unsigned long flags; + u32 tcon; + + spin_lock_irqsave(&samsung_pwm_lock, flags); + + tcon = readl(our_chip->base + REG_TCON); + tcon &= ~TCON_AUTORELOAD(tcon_chan); + writel(tcon, our_chip->base + REG_TCON); + + spin_unlock_irqrestore(&samsung_pwm_lock, flags); +} + +static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); + struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); + u32 tin_ns = chan->tin_ns, tcnt, tcmp; + + /* + * We currently avoid using 64bit arithmetic by using the + * fact that anything faster than 1Hz is easily representable + * by 32bits. + */ + if (period_ns > NSEC_PER_SEC) + return -ERANGE; + + if (period_ns == chan->period_ns && duty_ns == chan->duty_ns) + return 0; + + tcnt = readl(our_chip->base + REG_TCNTB(pwm->hwpwm)); + + /* We need tick count for calculation, not last tick. */ + ++tcnt; + + /* Check to see if we are changing the clock rate of the PWM. */ + if (chan->period_ns != period_ns) { + unsigned long tin_rate; + u32 period; + + period = NSEC_PER_SEC / period_ns; + + dev_dbg(our_chip->chip.dev, "duty_ns=%d, period_ns=%d (%u)\n", + duty_ns, period_ns, period); + + tin_rate = pwm_samsung_calc_tin(our_chip, pwm->hwpwm, period); + + dev_dbg(our_chip->chip.dev, "tin_rate=%lu\n", tin_rate); + + tin_ns = NSEC_PER_SEC / tin_rate; + tcnt = period_ns / tin_ns; + } + + /* Period is too short. */ + if (tcnt <= 1) + return -ERANGE; + + /* Note that counters count down. */ + tcmp = duty_ns / tin_ns; + + /* 0% duty is not available */ + if (!tcmp) + ++tcmp; + + tcmp = tcnt - tcmp; + + /* Decrement to get tick numbers, instead of tick counts. */ + --tcnt; + /* -1UL will give 100% duty. */ + --tcmp; + + dev_dbg(our_chip->chip.dev, + "tin_ns=%u, tcmp=%u/%u\n", tin_ns, tcmp, tcnt); + + /* Update PWM registers. */ + writel(tcnt, our_chip->base + REG_TCNTB(pwm->hwpwm)); + writel(tcmp, our_chip->base + REG_TCMPB(pwm->hwpwm)); + + if (test_bit(PWMF_ENABLED, &pwm->flags)) + pwm_samsung_enable(chip, pwm); + + chan->period_ns = period_ns; + chan->tin_ns = tin_ns; + chan->duty_ns = duty_ns; + + return 0; +} + +static void pwm_samsung_set_invert(struct samsung_pwm_chip *chip, + unsigned int channel, bool invert) +{ + unsigned int tcon_chan = to_tcon_channel(channel); + unsigned long flags; + u32 tcon; + + spin_lock_irqsave(&samsung_pwm_lock, flags); + + tcon = readl(chip->base + REG_TCON); + + if (invert) { + chip->inverter_mask |= BIT(channel); + tcon |= TCON_INVERT(tcon_chan); + } else { + chip->inverter_mask &= ~BIT(channel); + tcon &= ~TCON_INVERT(tcon_chan); + } + + writel(tcon, chip->base + REG_TCON); + + spin_unlock_irqrestore(&samsung_pwm_lock, flags); +} + +static int pwm_samsung_set_polarity(struct pwm_chip *chip, + struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); + bool invert = (polarity == PWM_POLARITY_NORMAL); + + /* Inverted means normal in the hardware. */ + pwm_samsung_set_invert(our_chip, pwm->hwpwm, invert); + + return 0; +} + +static const struct pwm_ops pwm_samsung_ops = { + .request = pwm_samsung_request, + .free = pwm_samsung_free, + .enable = pwm_samsung_enable, + .disable = pwm_samsung_disable, + .config = pwm_samsung_config, + .set_polarity = pwm_samsung_set_polarity, + .owner = THIS_MODULE, +}; + +#ifdef CONFIG_OF +static const struct samsung_pwm_variant s3c24xx_variant = { + .bits = 16, + .div_base = 1, + .has_tint_cstat = false, + .tclk_mask = BIT(4), +}; + +static const struct samsung_pwm_variant s3c64xx_variant = { + .bits = 32, + .div_base = 0, + .has_tint_cstat = true, + .tclk_mask = BIT(7) | BIT(6) | BIT(5), +}; + +static const struct samsung_pwm_variant s5p64x0_variant = { + .bits = 32, + .div_base = 0, + .has_tint_cstat = true, + .tclk_mask = 0, +}; + +static const struct samsung_pwm_variant s5pc100_variant = { + .bits = 32, + .div_base = 0, + .has_tint_cstat = true, + .tclk_mask = BIT(5), +}; + +static const struct of_device_id samsung_pwm_matches[] = { + { .compatible = "samsung,s3c2410-pwm", .data = &s3c24xx_variant }, + { .compatible = "samsung,s3c6400-pwm", .data = &s3c64xx_variant }, + { .compatible = "samsung,s5p6440-pwm", .data = &s5p64x0_variant }, + { .compatible = "samsung,s5pc100-pwm", .data = &s5pc100_variant }, + { .compatible = "samsung,exynos4210-pwm", .data = &s5p64x0_variant }, + {}, +}; + +static int pwm_samsung_parse_dt(struct samsung_pwm_chip *chip) +{ + struct device_node *np = chip->chip.dev->of_node; + const struct of_device_id *match; + struct property *prop; + const __be32 *cur; + u32 val; + + match = of_match_node(samsung_pwm_matches, np); + if (!match) + return -ENODEV; + + memcpy(&chip->variant, match->data, sizeof(chip->variant)); + + of_property_for_each_u32(np, "samsung,pwm-outputs", prop, cur, val) { + if (val >= SAMSUNG_PWM_NUM) { + dev_err(chip->chip.dev, + "%s: invalid channel index in samsung,pwm-outputs property\n", + __func__); + continue; + } + chip->variant.output_mask |= BIT(val); + } + + return 0; +} +#else +static int pwm_samsung_parse_dt(struct samsung_pwm_chip *chip) +{ + return -ENODEV; +} +#endif + +static int pwm_samsung_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct samsung_pwm_chip *chip; + struct resource *res; + unsigned int chan; + int ret; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->chip.dev = &pdev->dev; + chip->chip.ops = &pwm_samsung_ops; + chip->chip.base = -1; + chip->chip.npwm = SAMSUNG_PWM_NUM; + chip->inverter_mask = BIT(SAMSUNG_PWM_NUM) - 1; + + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { + ret = pwm_samsung_parse_dt(chip); + if (ret) + return ret; + + chip->chip.of_xlate = of_pwm_xlate_with_flags; + chip->chip.of_pwm_n_cells = 3; + } else { + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data specified\n"); + return -EINVAL; + } + + memcpy(&chip->variant, pdev->dev.platform_data, + sizeof(chip->variant)); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(chip->base)) + return PTR_ERR(chip->base); + + chip->base_clk = devm_clk_get(&pdev->dev, "timers"); + if (IS_ERR(chip->base_clk)) { + dev_err(dev, "failed to get timer base clk\n"); + return PTR_ERR(chip->base_clk); + } + + ret = clk_prepare_enable(chip->base_clk); + if (ret < 0) { + dev_err(dev, "failed to enable base clock\n"); + return ret; + } + + for (chan = 0; chan < SAMSUNG_PWM_NUM; ++chan) + if (chip->variant.output_mask & BIT(chan)) + pwm_samsung_set_invert(chip, chan, true); + + /* Following clocks are optional. */ + chip->tclk0 = devm_clk_get(&pdev->dev, "pwm-tclk0"); + chip->tclk1 = devm_clk_get(&pdev->dev, "pwm-tclk1"); + + platform_set_drvdata(pdev, chip); + + ret = pwmchip_add(&chip->chip); + if (ret < 0) { + dev_err(dev, "failed to register PWM chip\n"); + clk_disable_unprepare(chip->base_clk); + return ret; + } + + dev_dbg(dev, "base_clk at %lu, tclk0 at %lu, tclk1 at %lu\n", + clk_get_rate(chip->base_clk), + !IS_ERR(chip->tclk0) ? clk_get_rate(chip->tclk0) : 0, + !IS_ERR(chip->tclk1) ? clk_get_rate(chip->tclk1) : 0); + + return 0; +} + +static int pwm_samsung_remove(struct platform_device *pdev) +{ + struct samsung_pwm_chip *chip = platform_get_drvdata(pdev); + int ret; + + ret = pwmchip_remove(&chip->chip); + if (ret < 0) + return ret; + + clk_disable_unprepare(chip->base_clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int pwm_samsung_suspend(struct device *dev) +{ + struct samsung_pwm_chip *chip = dev_get_drvdata(dev); + unsigned int i; + + /* + * No one preserves these values during suspend so reset them. + * Otherwise driver leaves PWM unconfigured if same values are + * passed to pwm_config() next time. + */ + for (i = 0; i < SAMSUNG_PWM_NUM; ++i) { + struct pwm_device *pwm = &chip->chip.pwms[i]; + struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); + + if (!chan) + continue; + + chan->period_ns = 0; + chan->duty_ns = 0; + } + + return 0; +} + +static int pwm_samsung_resume(struct device *dev) +{ + struct samsung_pwm_chip *chip = dev_get_drvdata(dev); + unsigned int chan; + + /* + * Inverter setting must be preserved across suspend/resume + * as nobody really seems to configure it more than once. + */ + for (chan = 0; chan < SAMSUNG_PWM_NUM; ++chan) { + if (chip->variant.output_mask & BIT(chan)) + pwm_samsung_set_invert(chip, chan, + chip->inverter_mask & BIT(chan)); + } + + return 0; +} +#endif + +static const struct dev_pm_ops pwm_samsung_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pwm_samsung_suspend, pwm_samsung_resume) +}; + +static struct platform_driver pwm_samsung_driver = { + .driver = { + .name = "samsung-pwm", + .owner = THIS_MODULE, + .pm = &pwm_samsung_pm_ops, + .of_match_table = of_match_ptr(samsung_pwm_matches), + }, + .probe = pwm_samsung_probe, + .remove = pwm_samsung_remove, +}; +module_platform_driver(pwm_samsung_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomasz Figa "); +MODULE_ALIAS("platform:samsung-pwm"); diff --git a/include/clocksource/samsung_pwm.h b/include/clocksource/samsung_pwm.h index 5c449c8199e9..0c7d48b8b396 100644 --- a/include/clocksource/samsung_pwm.h +++ b/include/clocksource/samsung_pwm.h @@ -20,7 +20,14 @@ #define SAMSUNG_PWM_NUM 5 +/* + * Following declaration must be in an ifdef due to this symbol being static + * in pwm-samsung driver if the clocksource driver is not compiled in and the + * spinlock is not shared between both drivers. + */ +#ifdef CONFIG_CLKSRC_SAMSUNG_PWM extern spinlock_t samsung_pwm_lock; +#endif struct samsung_pwm_variant { u8 bits; -- cgit v1.2.3-71-gd317 From fa6fdb33b486a8afc5439c504da8d581e142c77d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 8 Aug 2013 15:22:55 -0700 Subject: driver core: bus_type: add dev_groups attribute groups are much more flexible than just a list of attributes, due to their support for visibility of the attributes, and binary attributes. Add dev_groups to struct bus_type which should be used instead of dev_attrs. dev_attrs will be removed from the structure soon. Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 5 +++++ drivers/base/bus.c | 6 ++++++ drivers/base/core.c | 7 +++---- include/linux/device.h | 4 +++- 4 files changed, 17 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/base/base.h b/drivers/base/base.h index b8bdfe61daa6..fccf954f82fd 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -119,6 +119,11 @@ static inline int driver_match_device(struct device_driver *drv, return drv->bus->match ? drv->bus->match(dev, drv) : 1; } +extern int device_add_groups(struct device *dev, + const struct attribute_group **groups); +extern void device_remove_groups(struct device *dev, + const struct attribute_group **groups); + extern char *make_class_name(const char *name, struct kobject *kobj); extern int devres_release_all(struct device *dev); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index d414331b480e..7b2dc5ba7d79 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -499,6 +499,9 @@ int bus_add_device(struct device *dev) error = device_add_attrs(bus, dev); if (error) goto out_put; + error = device_add_groups(dev, bus->dev_groups); + if (error) + goto out_groups; error = sysfs_create_link(&bus->p->devices_kset->kobj, &dev->kobj, dev_name(dev)); if (error) @@ -513,6 +516,8 @@ int bus_add_device(struct device *dev) out_subsys: sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev)); +out_groups: + device_remove_groups(dev, bus->dev_groups); out_id: device_remove_attrs(bus, dev); out_put: @@ -575,6 +580,7 @@ void bus_remove_device(struct device *dev) sysfs_remove_link(&dev->bus->p->devices_kset->kobj, dev_name(dev)); device_remove_attrs(dev->bus, dev); + device_remove_groups(dev, dev->bus->dev_groups); if (klist_node_attached(&dev->p->knode_bus)) klist_del(&dev->p->knode_bus); diff --git a/drivers/base/core.c b/drivers/base/core.c index e51c78782933..09a99d609644 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -491,8 +491,7 @@ static void device_remove_bin_attributes(struct device *dev, device_remove_bin_file(dev, &attrs[i]); } -static int device_add_groups(struct device *dev, - const struct attribute_group **groups) +int device_add_groups(struct device *dev, const struct attribute_group **groups) { int error = 0; int i; @@ -511,8 +510,8 @@ static int device_add_groups(struct device *dev, return error; } -static void device_remove_groups(struct device *dev, - const struct attribute_group **groups) +void device_remove_groups(struct device *dev, + const struct attribute_group **groups) { int i; diff --git a/include/linux/device.h b/include/linux/device.h index 22b546a58591..3aec4a68b1d4 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -66,6 +66,7 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * @bus_attrs: Default attributes of the bus. * @dev_attrs: Default attributes of the devices on the bus. * @drv_attrs: Default attributes of the device drivers on the bus. + * @dev_groups: Default attributes of the devices on the bus. * @match: Called, perhaps multiple times, whenever a new device or driver * is added for this bus. It should return a nonzero value if the * given device can be handled by the given driver. @@ -104,8 +105,9 @@ struct bus_type { const char *dev_name; struct device *dev_root; struct bus_attribute *bus_attrs; - struct device_attribute *dev_attrs; + struct device_attribute *dev_attrs; /* use dev_groups instead */ struct driver_attribute *drv_attrs; + const struct attribute_group **dev_groups; int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); -- cgit v1.2.3-71-gd317 From ed0617b5c0bcd7fd04053568aa0cc19a977a1f26 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 8 Aug 2013 15:22:56 -0700 Subject: driver core: bus_type: add drv_groups attribute groups are much more flexible than just a list of attributes, due to their support for visibility of the attributes, and binary attributes. Add drv_groups to struct bus_type which should be used instead of drv_attrs. drv_attrs will be removed from the structure soon. Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 5 +++++ drivers/base/bus.c | 5 +++++ drivers/base/driver.c | 8 ++++---- include/linux/device.h | 4 +++- 4 files changed, 17 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/base/base.h b/drivers/base/base.h index fccf954f82fd..2cbc6774f4cd 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -119,6 +119,11 @@ static inline int driver_match_device(struct device_driver *drv, return drv->bus->match ? drv->bus->match(dev, drv) : 1; } +extern int driver_add_groups(struct device_driver *drv, + const struct attribute_group **groups); +extern void driver_remove_groups(struct device_driver *drv, + const struct attribute_group **groups); + extern int device_add_groups(struct device *dev, const struct attribute_group **groups); extern void device_remove_groups(struct device *dev, diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 7b2dc5ba7d79..de5ce22df306 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -725,6 +725,10 @@ int bus_add_driver(struct device_driver *drv) printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", __func__, drv->name); } + error = driver_add_groups(drv, bus->drv_groups); + if (error) + printk(KERN_ERR "%s: driver_create_groups(%s) failed\n", + __func__, drv->name); if (!drv->suppress_bind_attrs) { error = add_bind_files(drv); @@ -762,6 +766,7 @@ void bus_remove_driver(struct device_driver *drv) if (!drv->suppress_bind_attrs) remove_bind_files(drv); driver_remove_attrs(drv->bus, drv); + driver_remove_groups(drv, drv->bus->drv_groups); driver_remove_file(drv, &driver_attr_uevent); klist_remove(&drv->p->knode_bus); pr_debug("bus: '%s': remove driver %s\n", drv->bus->name, drv->name); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 974e301a1ef0..89db726ebb98 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -123,8 +123,8 @@ void driver_remove_file(struct device_driver *drv, } EXPORT_SYMBOL_GPL(driver_remove_file); -static int driver_add_groups(struct device_driver *drv, - const struct attribute_group **groups) +int driver_add_groups(struct device_driver *drv, + const struct attribute_group **groups) { int error = 0; int i; @@ -143,8 +143,8 @@ static int driver_add_groups(struct device_driver *drv, return error; } -static void driver_remove_groups(struct device_driver *drv, - const struct attribute_group **groups) +void driver_remove_groups(struct device_driver *drv, + const struct attribute_group **groups) { int i; diff --git a/include/linux/device.h b/include/linux/device.h index 3aec4a68b1d4..94d987f128bb 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -67,6 +67,7 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * @dev_attrs: Default attributes of the devices on the bus. * @drv_attrs: Default attributes of the device drivers on the bus. * @dev_groups: Default attributes of the devices on the bus. + * @drv_groups: Default attributes of the device drivers on the bus. * @match: Called, perhaps multiple times, whenever a new device or driver * is added for this bus. It should return a nonzero value if the * given device can be handled by the given driver. @@ -106,8 +107,9 @@ struct bus_type { struct device *dev_root; struct bus_attribute *bus_attrs; struct device_attribute *dev_attrs; /* use dev_groups instead */ - struct driver_attribute *drv_attrs; + struct driver_attribute *drv_attrs; /* use drv_groups instead */ const struct attribute_group **dev_groups; + const struct attribute_group **drv_groups; int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); -- cgit v1.2.3-71-gd317 From 12478ba077adf8b53be6101b80dd8a65e4df9ea6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 8 Aug 2013 15:22:57 -0700 Subject: driver core: bus_type: add bus_groups attribute groups are much more flexible than just a list of attributes, due to their support for visibility of the attributes, and binary attributes. Add bus_groups to struct bus_type which should be used instead of bus_attrs. bus_attrs will be removed from the structure soon. Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/device.h | 4 +++- 2 files changed, 40 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index de5ce22df306..5ee5d3c1d74b 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -881,6 +881,37 @@ static void bus_remove_attrs(struct bus_type *bus) } } +static int bus_add_groups(struct bus_type *bus, + const struct attribute_group **groups) +{ + int error = 0; + int i; + + if (groups) { + for (i = 0; groups[i]; i++) { + error = sysfs_create_group(&bus->p->subsys.kobj, + groups[i]); + if (error) { + while (--i >= 0) + sysfs_remove_group(&bus->p->subsys.kobj, + groups[i]); + break; + } + } + } + return error; +} + +static void bus_remove_groups(struct bus_type *bus, + const struct attribute_group **groups) +{ + int i; + + if (groups) + for (i = 0; groups[i]; i++) + sysfs_remove_group(&bus->p->subsys.kobj, groups[i]); +} + static void klist_devices_get(struct klist_node *n) { struct device_private *dev_prv = to_device_private_bus(n); @@ -973,10 +1004,15 @@ int bus_register(struct bus_type *bus) retval = bus_add_attrs(bus); if (retval) goto bus_attrs_fail; + retval = bus_add_groups(bus, bus->bus_groups); + if (retval) + goto bus_groups_fail; pr_debug("bus: '%s': registered\n", bus->name); return 0; +bus_groups_fail: + bus_remove_attrs(bus); bus_attrs_fail: remove_probe_files(bus); bus_probe_files_fail: @@ -1007,6 +1043,7 @@ void bus_unregister(struct bus_type *bus) if (bus->dev_root) device_unregister(bus->dev_root); bus_remove_attrs(bus); + bus_remove_groups(bus, bus->bus_groups); remove_probe_files(bus); kset_unregister(bus->p->drivers_kset); kset_unregister(bus->p->devices_kset); diff --git a/include/linux/device.h b/include/linux/device.h index 94d987f128bb..26e23b22ed0a 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -66,6 +66,7 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * @bus_attrs: Default attributes of the bus. * @dev_attrs: Default attributes of the devices on the bus. * @drv_attrs: Default attributes of the device drivers on the bus. + * @bus_groups: Default attributes of the bus. * @dev_groups: Default attributes of the devices on the bus. * @drv_groups: Default attributes of the device drivers on the bus. * @match: Called, perhaps multiple times, whenever a new device or driver @@ -105,9 +106,10 @@ struct bus_type { const char *name; const char *dev_name; struct device *dev_root; - struct bus_attribute *bus_attrs; + struct bus_attribute *bus_attrs; /* use bus_groups instead */ struct device_attribute *dev_attrs; /* use dev_groups instead */ struct driver_attribute *drv_attrs; /* use drv_groups instead */ + const struct attribute_group **bus_groups; const struct attribute_group **dev_groups; const struct attribute_group **drv_groups; -- cgit v1.2.3-71-gd317 From 2d854e5738cded368a0759f85b1197f5c044513d Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 12 Jul 2013 19:02:30 +0200 Subject: context_tracing: Fix guest accounting with native vtime 1) If context tracking is enabled with native vtime accounting (which combo is useless except for dev testing), we call vtime_guest_enter() and vtime_guest_exit() on host <-> guest switches. But those are stubs in this configurations. As a result, cputime is not correctly flushed on kvm context switches. 2) If context tracking runs but is disabled on some CPUs, those CPUs end up calling __guest_enter/__guest_exit which in turn call vtime_account_system(). We don't want to call this because we run in tick based accounting for these CPUs. Refactor the guest_enter/guest_exit code such that all combinations finally work. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman --- include/linux/context_tracking.h | 52 +++++++++++++++++----------------------- kernel/context_tracking.c | 6 +++-- 2 files changed, 26 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index fc09d7b0dacf..5984f2556d13 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -20,25 +20,6 @@ struct context_tracking { } state; }; -static inline void __guest_enter(void) -{ - /* - * This is running in ioctl context so we can avoid - * the call to vtime_account() with its unnecessary idle check. - */ - vtime_account_system(current); - current->flags |= PF_VCPU; -} - -static inline void __guest_exit(void) -{ - /* - * This is running in ioctl context so we can avoid - * the call to vtime_account() with its unnecessary idle check. - */ - vtime_account_system(current); - current->flags &= ~PF_VCPU; -} #ifdef CONFIG_CONTEXT_TRACKING DECLARE_PER_CPU(struct context_tracking, context_tracking); @@ -56,9 +37,6 @@ static inline bool context_tracking_active(void) extern void user_enter(void); extern void user_exit(void); -extern void guest_enter(void); -extern void guest_exit(void); - static inline enum ctx_state exception_enter(void) { enum ctx_state prev_ctx; @@ -81,21 +59,35 @@ extern void context_tracking_task_switch(struct task_struct *prev, static inline bool context_tracking_in_user(void) { return false; } static inline void user_enter(void) { } static inline void user_exit(void) { } +static inline enum ctx_state exception_enter(void) { return 0; } +static inline void exception_exit(enum ctx_state prev_ctx) { } +static inline void context_tracking_task_switch(struct task_struct *prev, + struct task_struct *next) { } +#endif /* !CONFIG_CONTEXT_TRACKING */ +#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN +extern void guest_enter(void); +extern void guest_exit(void); +#else static inline void guest_enter(void) { - __guest_enter(); + /* + * This is running in ioctl context so we can avoid + * the call to vtime_account() with its unnecessary idle check. + */ + vtime_account_system(current); + current->flags |= PF_VCPU; } static inline void guest_exit(void) { - __guest_exit(); + /* + * This is running in ioctl context so we can avoid + * the call to vtime_account() with its unnecessary idle check. + */ + vtime_account_system(current); + current->flags &= ~PF_VCPU; } - -static inline enum ctx_state exception_enter(void) { return 0; } -static inline void exception_exit(enum ctx_state prev_ctx) { } -static inline void context_tracking_task_switch(struct task_struct *prev, - struct task_struct *next) { } -#endif /* !CONFIG_CONTEXT_TRACKING */ +#endif /* CONFIG_VIRT_CPU_ACCOUNTING_GEN */ #endif diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index 942835c12ae5..1f47119c5b09 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c @@ -141,12 +141,13 @@ void user_exit(void) local_irq_restore(flags); } +#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN void guest_enter(void) { if (vtime_accounting_enabled()) vtime_guest_enter(current); else - __guest_enter(); + current->flags |= PF_VCPU; } EXPORT_SYMBOL_GPL(guest_enter); @@ -155,9 +156,10 @@ void guest_exit(void) if (vtime_accounting_enabled()) vtime_guest_exit(current); else - __guest_exit(); + current->flags &= ~PF_VCPU; } EXPORT_SYMBOL_GPL(guest_exit); +#endif /* CONFIG_VIRT_CPU_ACCOUNTING_GEN */ /** -- cgit v1.2.3-71-gd317 From 5b206d48e58204e84d249c4eb18651a1ff7a1274 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 12 Jul 2013 19:05:14 +0200 Subject: vtime: Update a few comments Update a stale comment from the old vtime era and document some locking that might be non obvious. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman --- include/linux/context_tracking.h | 10 ++++------ kernel/sched/cputime.c | 7 +++++++ 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index 5984f2556d13..d883ff0dd8fc 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -72,8 +72,9 @@ extern void guest_exit(void); static inline void guest_enter(void) { /* - * This is running in ioctl context so we can avoid - * the call to vtime_account() with its unnecessary idle check. + * This is running in ioctl context so its safe + * to assume that it's the stime pending cputime + * to flush. */ vtime_account_system(current); current->flags |= PF_VCPU; @@ -81,10 +82,7 @@ static inline void guest_enter(void) static inline void guest_exit(void) { - /* - * This is running in ioctl context so we can avoid - * the call to vtime_account() with its unnecessary idle check. - */ + /* Flush the guest cputime we spent on the guest */ vtime_account_system(current); current->flags &= ~PF_VCPU; } diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index a7959e05a9d5..223a35efa0a6 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -712,6 +712,13 @@ void vtime_user_enter(struct task_struct *tsk) void vtime_guest_enter(struct task_struct *tsk) { + /* + * The flags must be updated under the lock with + * the vtime_snap flush and update. + * That enforces a right ordering and update sequence + * synchronization against the reader (task_gtime()) + * that can thus safely catch up with a tickless delta. + */ write_seqlock(&tsk->vtime_seqlock); __vtime_account_system(tsk); current->flags |= PF_VCPU; -- cgit v1.2.3-71-gd317 From 2e70933866ace52091a3c11a5c104c063ab0c445 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 10 Jul 2013 00:55:25 +0200 Subject: nohz: Only enable context tracking on full dynticks CPUs The context tracking subsystem has the ability to selectively enable the tracking on any defined subset of CPU. This means that we can define a CPU range that doesn't run the context tracking and another range that does. Now what we want in practice is to enable the tracking on full dynticks CPUs only. In order to perform this, we just need to pass our full dynticks CPU range selection from the full dynticks subsystem to the context tracking. This way we can spare the overhead of RCU user extended quiescent state and vtime maintainance on the CPUs that are outside the full dynticks range. Just keep in mind the raw context tracking itself is still necessary everywhere. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman --- include/linux/context_tracking.h | 2 ++ kernel/context_tracking.c | 5 +++++ kernel/time/tick-sched.c | 6 ++++++ 3 files changed, 13 insertions(+) (limited to 'include') diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index d883ff0dd8fc..1ae37c708c63 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -34,6 +34,8 @@ static inline bool context_tracking_active(void) return __this_cpu_read(context_tracking.active); } +extern void context_tracking_cpu_set(int cpu); + extern void user_enter(void); extern void user_exit(void); diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index 7b095de356c5..72bcb2570d3e 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c @@ -26,6 +26,11 @@ DEFINE_PER_CPU(struct context_tracking, context_tracking) = { #endif }; +void context_tracking_cpu_set(int cpu) +{ + per_cpu(context_tracking.active, cpu) = true; +} + /** * user_enter - Inform the context tracking that the CPU is going to * enter userspace mode. diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 9563c744dad2..91a2528b5f44 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -344,11 +345,16 @@ static int tick_nohz_init_all(void) void __init tick_nohz_init(void) { + int cpu; + if (!have_nohz_full_mask) { if (tick_nohz_init_all() < 0) return; } + for_each_cpu(cpu, nohz_full_mask) + context_tracking_cpu_set(cpu); + cpu_notifier(tick_nohz_cpu_down_callback, 0); cpulist_scnprintf(nohz_full_buf, sizeof(nohz_full_buf), nohz_full_mask); pr_info("NO_HZ: Full dynticks CPUs: %s.\n", nohz_full_buf); -- cgit v1.2.3-71-gd317 From b9d10be7a8e88fdcb12540387c219cdde87b0795 Mon Sep 17 00:00:00 2001 From: Toshi Kani Date: Mon, 12 Aug 2013 09:45:53 -0600 Subject: ACPI / processor: Acquire writer lock to update CPU maps CPU system maps are protected with reader/writer locks. The reader lock, get_online_cpus(), assures that the maps are not updated while holding the lock. The writer lock, cpu_hotplug_begin(), is used to udpate the cpu maps along with cpu_maps_update_begin(). However, the ACPI processor handler updates the cpu maps without holding the the writer lock. acpi_map_lsapic() is called from acpi_processor_hotadd_init() to update cpu_possible_mask and cpu_present_mask. acpi_unmap_lsapic() is called from acpi_processor_remove() to update cpu_possible_mask. Currently, they are either unprotected or protected with the reader lock, which is not correct. For example, the get_online_cpus() below is supposed to assure that cpu_possible_mask is not changed while the code is iterating with for_each_possible_cpu(). get_online_cpus(); for_each_possible_cpu(cpu) { : } put_online_cpus(); However, this lock has no protection with CPU hotplug since the ACPI processor handler does not use the writer lock when it updates cpu_possible_mask. The reader lock does not serialize within the readers. This patch protects them with the writer lock with cpu_hotplug_begin() along with cpu_maps_update_begin(), which must be held before calling cpu_hotplug_begin(). It also protects arch_register_cpu() / arch_unregister_cpu(), which creates / deletes a sysfs cpu device interface. For this purpose it changes cpu_hotplug_begin() and cpu_hotplug_done() to global and exports them in cpu.h. Signed-off-by: Toshi Kani Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_processor.c | 21 ++++++++++++++++----- include/linux/cpu.h | 4 ++++ kernel/cpu.c | 9 +++------ 3 files changed, 23 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 5a74a9c1e42c..f29e06efa479 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -178,14 +178,17 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) return -ENODEV; + cpu_maps_update_begin(); + cpu_hotplug_begin(); + ret = acpi_map_lsapic(pr->handle, &pr->id); if (ret) - return ret; + goto out; ret = arch_register_cpu(pr->id); if (ret) { acpi_unmap_lsapic(pr->id); - return ret; + goto out; } /* @@ -195,7 +198,11 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) */ pr_info("CPU%d has been hot-added\n", pr->id); pr->flags.need_hotplug_init = 1; - return 0; + +out: + cpu_hotplug_done(); + cpu_maps_update_done(); + return ret; } #else static inline int acpi_processor_hotadd_init(struct acpi_processor *pr) @@ -452,11 +459,15 @@ static void acpi_processor_remove(struct acpi_device *device) per_cpu(processor_device_array, pr->id) = NULL; per_cpu(processors, pr->id) = NULL; + cpu_maps_update_begin(); + cpu_hotplug_begin(); + /* Remove the CPU. */ - get_online_cpus(); arch_unregister_cpu(pr->id); acpi_unmap_lsapic(pr->id); - put_online_cpus(); + + cpu_hotplug_done(); + cpu_maps_update_done(); try_offline_node(cpu_to_node(pr->id)); diff --git a/include/linux/cpu.h b/include/linux/cpu.h index ab0eade73039..956c0a16566f 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -172,6 +172,8 @@ extern struct bus_type cpu_subsys; #ifdef CONFIG_HOTPLUG_CPU /* Stop CPUs going up and down. */ +extern void cpu_hotplug_begin(void); +extern void cpu_hotplug_done(void); extern void get_online_cpus(void); extern void put_online_cpus(void); extern void cpu_hotplug_disable(void); @@ -197,6 +199,8 @@ static inline void cpu_hotplug_driver_unlock(void) #else /* CONFIG_HOTPLUG_CPU */ +static inline void cpu_hotplug_begin(void) {} +static inline void cpu_hotplug_done(void) {} #define get_online_cpus() do { } while (0) #define put_online_cpus() do { } while (0) #define cpu_hotplug_disable() do { } while (0) diff --git a/kernel/cpu.c b/kernel/cpu.c index b2b227b82123..d7f07a2da5a6 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -113,7 +113,7 @@ EXPORT_SYMBOL_GPL(put_online_cpus); * get_online_cpus() not an api which is called all that often. * */ -static void cpu_hotplug_begin(void) +void cpu_hotplug_begin(void) { cpu_hotplug.active_writer = current; @@ -127,7 +127,7 @@ static void cpu_hotplug_begin(void) } } -static void cpu_hotplug_done(void) +void cpu_hotplug_done(void) { cpu_hotplug.active_writer = NULL; mutex_unlock(&cpu_hotplug.lock); @@ -154,10 +154,7 @@ void cpu_hotplug_enable(void) cpu_maps_update_done(); } -#else /* #if CONFIG_HOTPLUG_CPU */ -static void cpu_hotplug_begin(void) {} -static void cpu_hotplug_done(void) {} -#endif /* #else #if CONFIG_HOTPLUG_CPU */ +#endif /* CONFIG_HOTPLUG_CPU */ /* Need to know about CPUs going up/down? */ int __ref register_cpu_notifier(struct notifier_block *nb) -- cgit v1.2.3-71-gd317 From 7bb587f4eef8f71ce589f360ab99bb54ab0fc85d Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Fri, 28 Jun 2013 20:39:12 +0800 Subject: dmaengine: add interface of dma_get_slave_channel Suggested by Arnd, add dma_get_slave_channel interface Dma host driver could get specific channel specificied by request line, rather than filter. host example: static struct dma_chan *xx_of_dma_simple_xlate(struct of_phandle_args *dma_spec, struct of_dma *ofdma) { struct xx_dma_dev *d = ofdma->of_dma_data; unsigned int request = dma_spec->args[0]; if (request > d->dma_requests) return NULL; return dma_get_slave_channel(&(d->chans[request].vc.chan)); } probe: of_dma_controller_register((&op->dev)->of_node, xx_of_dma_simple_xlate, d); Signed-off-by: Zhangfei Gao Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 26 ++++++++++++++++++++++++++ include/linux/dmaengine.h | 1 + 2 files changed, 27 insertions(+) (limited to 'include') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 9e56745f87bf..5932ab164ace 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -503,6 +503,32 @@ static struct dma_chan *private_candidate(const dma_cap_mask_t *mask, return NULL; } +/** + * dma_request_channel - try to get specific channel exclusively + * @chan: target channel + */ +struct dma_chan *dma_get_slave_channel(struct dma_chan *chan) +{ + int err = -EBUSY; + + /* lock against __dma_request_channel */ + mutex_lock(&dma_list_mutex); + + if (chan->client_count == 0) + err = dma_chan_get(chan); + else + chan = NULL; + + mutex_unlock(&dma_list_mutex); + + if (err) + pr_debug("%s: failed to get %s: (%d)\n", + __func__, dma_chan_name(chan), err); + + return chan; +} +EXPORT_SYMBOL_GPL(dma_get_slave_channel); + /** * dma_request_channel - try to allocate an exclusive channel * @mask: capabilities that the channel must satisfy diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index cb286b1acdb6..c271608e862e 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -995,6 +995,7 @@ int dma_async_device_register(struct dma_device *device); void dma_async_device_unregister(struct dma_device *device); void dma_run_dependencies(struct dma_async_tx_descriptor *tx); struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type); +struct dma_chan *dma_get_slave_channel(struct dma_chan *chan); struct dma_chan *net_dma_find_channel(void); #define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y) #define dma_request_slave_channel_compat(mask, x, y, dev, name) \ -- cgit v1.2.3-71-gd317 From 2641f540800b47a1b9e9d8ce9d682fc15df088e2 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 8 Aug 2013 15:29:46 +0800 Subject: ACPICA: Fix divergences of the commit - ACPICA: Expose OSI version. The original commit 242b2287cd7f27521c8b54a4101d569e53e7a0ca "ACPICA: expose OSI version" triggers build errors in ACPICA when it is back ported. The patch removes the divergences between Linux and upstream ACPICA resulting from that. Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acglobal.h | 7 ++++++- drivers/acpi/acpica/utglobal.c | 1 - include/acpi/actypes.h | 28 +++++++++++++--------------- 3 files changed, 19 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index b8d38117a20c..90e846f985fa 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -138,6 +138,12 @@ u8 ACPI_INIT_GLOBAL(acpi_gbl_disable_auto_repair, FALSE); */ u8 ACPI_INIT_GLOBAL(acpi_gbl_disable_ssdt_table_load, FALSE); +/* + * We keep track of the latest version of Windows that has been requested by + * the BIOS. + */ +u8 ACPI_INIT_GLOBAL(acpi_gbl_osi_data, 0); + /* acpi_gbl_FADT is a local copy of the FADT, converted to a common format. */ struct acpi_table_fadt acpi_gbl_FADT; @@ -285,7 +291,6 @@ ACPI_EXTERN u8 acpi_gbl_debugger_configuration; ACPI_EXTERN u8 acpi_gbl_step_to_next_call; ACPI_EXTERN u8 acpi_gbl_acpi_hardware_present; ACPI_EXTERN u8 acpi_gbl_events_initialized; -ACPI_EXTERN u8 acpi_gbl_osi_data; ACPI_EXTERN struct acpi_interface_info *acpi_gbl_supported_interfaces; ACPI_EXTERN struct acpi_address_range *acpi_gbl_address_range_list[ACPI_ADDRESS_RANGE_MAX]; diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index f736448a8606..d6f26bf8a062 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -336,7 +336,6 @@ acpi_status acpi_ut_init_globals(void) acpi_gbl_trace_dbg_layer = 0; acpi_gbl_debugger_configuration = DEBUGGER_THREADING; acpi_gbl_db_output_flags = ACPI_DB_CONSOLE_OUTPUT; - acpi_gbl_osi_data = 0; acpi_gbl_osi_mutex = NULL; acpi_gbl_reg_methods_executed = FALSE; diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index eae55fb74905..b748aefce929 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -1137,7 +1137,19 @@ struct acpi_memory_list { #endif }; -/* Definitions for _OSI support */ +/* Definitions of _OSI support */ + +#define ACPI_VENDOR_STRINGS 0x01 +#define ACPI_FEATURE_STRINGS 0x02 +#define ACPI_ENABLE_INTERFACES 0x00 +#define ACPI_DISABLE_INTERFACES 0x04 + +#define ACPI_DISABLE_ALL_VENDOR_STRINGS (ACPI_DISABLE_INTERFACES | ACPI_VENDOR_STRINGS) +#define ACPI_DISABLE_ALL_FEATURE_STRINGS (ACPI_DISABLE_INTERFACES | ACPI_FEATURE_STRINGS) +#define ACPI_DISABLE_ALL_STRINGS (ACPI_DISABLE_INTERFACES | ACPI_VENDOR_STRINGS | ACPI_FEATURE_STRINGS) +#define ACPI_ENABLE_ALL_VENDOR_STRINGS (ACPI_ENABLE_INTERFACES | ACPI_VENDOR_STRINGS) +#define ACPI_ENABLE_ALL_FEATURE_STRINGS (ACPI_ENABLE_INTERFACES | ACPI_FEATURE_STRINGS) +#define ACPI_ENABLE_ALL_STRINGS (ACPI_ENABLE_INTERFACES | ACPI_VENDOR_STRINGS | ACPI_FEATURE_STRINGS) #define ACPI_OSI_WIN_2000 0x01 #define ACPI_OSI_WIN_XP 0x02 @@ -1152,18 +1164,4 @@ struct acpi_memory_list { #define ACPI_OSI_WIN_7 0x0B #define ACPI_OSI_WIN_8 0x0C -/* _OSI update actions */ - -#define ACPI_VENDOR_STRINGS 0x01 -#define ACPI_FEATURE_STRINGS 0x02 -#define ACPI_ENABLE_INTERFACES 0x00 -#define ACPI_DISABLE_INTERFACES 0x04 - -#define ACPI_DISABLE_ALL_VENDOR_STRINGS (ACPI_DISABLE_INTERFACES | ACPI_VENDOR_STRINGS) -#define ACPI_DISABLE_ALL_FEATURE_STRINGS (ACPI_DISABLE_INTERFACES | ACPI_FEATURE_STRINGS) -#define ACPI_DISABLE_ALL_STRINGS (ACPI_DISABLE_INTERFACES | ACPI_VENDOR_STRINGS | ACPI_FEATURE_STRINGS) -#define ACPI_ENABLE_ALL_VENDOR_STRINGS (ACPI_ENABLE_INTERFACES | ACPI_VENDOR_STRINGS) -#define ACPI_ENABLE_ALL_FEATURE_STRINGS (ACPI_ENABLE_INTERFACES | ACPI_FEATURE_STRINGS) -#define ACPI_ENABLE_ALL_STRINGS (ACPI_ENABLE_INTERFACES | ACPI_VENDOR_STRINGS | ACPI_FEATURE_STRINGS) - #endif /* __ACTYPES_H__ */ -- cgit v1.2.3-71-gd317 From 4ef17507834d19ba4fc24c4c1c61e8f68df356a6 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 8 Aug 2013 15:30:05 +0800 Subject: ACPICA: Update names for walk_namespace callbacks to clarify usage. Use of "preorder" and "postorder" was incorrect. The callbacks are simply invoked during tree ascent and descent during the depth-first walk. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/acnamesp.h | 4 ++-- drivers/acpi/acpica/nswalk.c | 26 +++++++++++++------------- drivers/acpi/acpica/nsxfeval.c | 16 ++++++++-------- include/acpi/acpixf.h | 4 ++-- 4 files changed, 25 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index b83dc32a5ae0..40b04bd5579e 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -104,8 +104,8 @@ acpi_ns_walk_namespace(acpi_object_type type, acpi_handle start_object, u32 max_depth, u32 flags, - acpi_walk_callback pre_order_visit, - acpi_walk_callback post_order_visit, + acpi_walk_callback descending_callback, + acpi_walk_callback ascending_callback, void *context, void **return_value); struct acpi_namespace_node *acpi_ns_get_next_node(struct acpi_namespace_node diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c index e70911a9e441..e81f15ef659a 100644 --- a/drivers/acpi/acpica/nswalk.c +++ b/drivers/acpi/acpica/nswalk.c @@ -156,9 +156,9 @@ struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type, * max_depth - Depth to which search is to reach * flags - Whether to unlock the NS before invoking * the callback routine - * pre_order_visit - Called during tree pre-order visit + * descending_callback - Called during tree descent * when an object of "Type" is found - * post_order_visit - Called during tree post-order visit + * ascending_callback - Called during tree ascent * when an object of "Type" is found * context - Passed to user function(s) above * return_value - from the user_function if terminated @@ -185,8 +185,8 @@ acpi_ns_walk_namespace(acpi_object_type type, acpi_handle start_node, u32 max_depth, u32 flags, - acpi_walk_callback pre_order_visit, - acpi_walk_callback post_order_visit, + acpi_walk_callback descending_callback, + acpi_walk_callback ascending_callback, void *context, void **return_value) { acpi_status status; @@ -255,22 +255,22 @@ acpi_ns_walk_namespace(acpi_object_type type, } /* - * Invoke the user function, either pre-order or post-order + * Invoke the user function, either descending, ascending, * or both. */ if (!node_previously_visited) { - if (pre_order_visit) { + if (descending_callback) { status = - pre_order_visit(child_node, level, - context, - return_value); + descending_callback(child_node, + level, context, + return_value); } } else { - if (post_order_visit) { + if (ascending_callback) { status = - post_order_visit(child_node, level, - context, - return_value); + ascending_callback(child_node, + level, context, + return_value); } } diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c index f553cfdb71dd..b38b4b07f86e 100644 --- a/drivers/acpi/acpica/nsxfeval.c +++ b/drivers/acpi/acpica/nsxfeval.c @@ -533,9 +533,9 @@ static void acpi_ns_resolve_references(struct acpi_evaluate_info *info) * PARAMETERS: type - acpi_object_type to search for * start_object - Handle in namespace where search begins * max_depth - Depth to which search is to reach - * pre_order_visit - Called during tree pre-order visit + * descending_callback - Called during tree descent * when an object of "Type" is found - * post_order_visit - Called during tree post-order visit + * ascending_callback - Called during tree ascent * when an object of "Type" is found * context - Passed to user function(s) above * return_value - Location where return value of @@ -563,8 +563,8 @@ acpi_status acpi_walk_namespace(acpi_object_type type, acpi_handle start_object, u32 max_depth, - acpi_walk_callback pre_order_visit, - acpi_walk_callback post_order_visit, + acpi_walk_callback descending_callback, + acpi_walk_callback ascending_callback, void *context, void **return_value) { acpi_status status; @@ -574,7 +574,7 @@ acpi_walk_namespace(acpi_object_type type, /* Parameter validation */ if ((type > ACPI_TYPE_LOCAL_MAX) || - (!max_depth) || (!pre_order_visit && !post_order_visit)) { + (!max_depth) || (!descending_callback && !ascending_callback)) { return_ACPI_STATUS(AE_BAD_PARAMETER); } @@ -606,9 +606,9 @@ acpi_walk_namespace(acpi_object_type type, } status = acpi_ns_walk_namespace(type, start_object, max_depth, - ACPI_NS_WALK_UNLOCK, pre_order_visit, - post_order_visit, context, - return_value); + ACPI_NS_WALK_UNLOCK, + descending_callback, ascending_callback, + context, return_value); (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 0dd03f226a68..e53f16a07237 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -212,8 +212,8 @@ acpi_status acpi_walk_namespace(acpi_object_type type, acpi_handle start_object, u32 max_depth, - acpi_walk_callback pre_order_visit, - acpi_walk_callback post_order_visit, + acpi_walk_callback descending_callback, + acpi_walk_callback ascending_callback, void *context, void **return_value); acpi_status -- cgit v1.2.3-71-gd317 From 36b99b22877bd25162239f1b00477ee6ae2d01ee Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 8 Aug 2013 15:30:11 +0800 Subject: ACPICA: Update version to 20130725. Version 20130725. Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index e53f16a07237..85bfdbe17805 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -46,7 +46,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20130626 +#define ACPI_CA_VERSION 0x20130725 #include #include -- cgit v1.2.3-71-gd317 From 4ca0c0d4784fa82d68733f7793e3487023e12282 Mon Sep 17 00:00:00 2001 From: Padmavathi Venna Date: Mon, 12 Aug 2013 15:19:52 +0530 Subject: ASoC: Samsung: I2S: Modify the I2S driver to support I2S on Exynos5420 Exynos5420 added support for I2S TDM mode. For this, there are some register changes in the I2S controller. This patch adds the relevant register changes to support I2S in normal mode. This patch adds a quirk for TDM mode and if TDM mode is present all the relevent changes will be applied. Signed-off-by: Padmavathi Venna Reviewed-by: Tomasz Figa Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/samsung-i2s.txt | 4 ++ include/linux/platform_data/asoc-s3c.h | 1 + sound/soc/samsung/i2s-regs.h | 15 ++++ sound/soc/samsung/i2s.c | 81 +++++++++++++++++++--- 4 files changed, 93 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt index 25a0024d1b0a..7386d444ada1 100644 --- a/Documentation/devicetree/bindings/sound/samsung-i2s.txt +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt @@ -6,6 +6,10 @@ Required SoC Specific Properties: - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S. - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with secondary fifo, s/w reset control and internal mux for root clk src. + - samsung,exynos5420-i2s: for 8/16/24bit multichannel(7.1) I2S with + secondary fifo, s/w reset control, internal mux for root clk src and + TDM support. TDM (Time division multiplexing) is to allow transfer of + multiple channel audio data on single data line. - reg: physical base address of the controller and length of memory mapped region. diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h index 88272591a895..9efc04dd255a 100644 --- a/include/linux/platform_data/asoc-s3c.h +++ b/include/linux/platform_data/asoc-s3c.h @@ -36,6 +36,7 @@ struct samsung_i2s { */ #define QUIRK_NO_MUXPSR (1 << 2) #define QUIRK_NEED_RSTCLR (1 << 3) +#define QUIRK_SUPPORTS_TDM (1 << 4) /* Quirks of the I2S controller */ u32 quirks; dma_addr_t idma_addr; diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h index 30513b7ede3a..821a50231002 100644 --- a/sound/soc/samsung/i2s-regs.h +++ b/sound/soc/samsung/i2s-regs.h @@ -31,6 +31,10 @@ #define I2SLVL1ADDR 0x34 #define I2SLVL2ADDR 0x38 #define I2SLVL3ADDR 0x3c +#define I2SSTR1 0x40 +#define I2SVER 0x44 +#define I2SFIC2 0x48 +#define I2STDM 0x4c #define CON_RSTCLR (1 << 31) #define CON_FRXOFSTATUS (1 << 26) @@ -117,6 +121,17 @@ #define MOD_BCLK_MASK 3 #define MOD_8BIT (1 << 0) +#define EXYNOS5420_MOD_LRP_SHIFT 15 +#define EXYNOS5420_MOD_SDF_SHIFT 6 +#define EXYNOS5420_MOD_RCLK_SHIFT 4 +#define EXYNOS5420_MOD_BCLK_SHIFT 0 +#define EXYNOS5420_MOD_BCLK_64FS 4 +#define EXYNOS5420_MOD_BCLK_96FS 5 +#define EXYNOS5420_MOD_BCLK_128FS 6 +#define EXYNOS5420_MOD_BCLK_192FS 7 +#define EXYNOS5420_MOD_BCLK_256FS 8 +#define EXYNOS5420_MOD_BCLK_MASK 0xf + #define MOD_CDCLKCON (1 << 12) #define PSR_PSREN (1 << 15) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 3b4835a1bd23..dd995a7ab55c 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -199,7 +199,12 @@ static inline bool is_manager(struct i2s_dai *i2s) /* Read RCLK of I2S (in multiples of LRCLK) */ static inline unsigned get_rfs(struct i2s_dai *i2s) { - u32 rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT); + u32 rfs; + + if (i2s->quirks & QUIRK_SUPPORTS_TDM) + rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT; + else + rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT); rfs &= MOD_RCLK_MASK; switch (rfs) { @@ -214,8 +219,12 @@ static inline unsigned get_rfs(struct i2s_dai *i2s) static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) { u32 mod = readl(i2s->addr + I2SMOD); - int rfs_shift = MOD_RCLK_SHIFT; + int rfs_shift; + if (i2s->quirks & QUIRK_SUPPORTS_TDM) + rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT; + else + rfs_shift = MOD_RCLK_SHIFT; mod &= ~(MOD_RCLK_MASK << rfs_shift); switch (rfs) { @@ -239,10 +248,22 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) /* Read Bit-Clock of I2S (in multiples of LRCLK) */ static inline unsigned get_bfs(struct i2s_dai *i2s) { - u32 bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT; - bfs &= MOD_BCLK_MASK; + u32 bfs; + + if (i2s->quirks & QUIRK_SUPPORTS_TDM) { + bfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_BCLK_SHIFT; + bfs &= EXYNOS5420_MOD_BCLK_MASK; + } else { + bfs = readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT; + bfs &= MOD_BCLK_MASK; + } switch (bfs) { + case 8: return 256; + case 7: return 192; + case 6: return 128; + case 5: return 96; + case 4: return 64; case 3: return 24; case 2: return 16; case 1: return 48; @@ -254,9 +275,22 @@ static inline unsigned get_bfs(struct i2s_dai *i2s) static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) { u32 mod = readl(i2s->addr + I2SMOD); - int bfs_shift = MOD_BCLK_SHIFT; + int bfs_shift; + int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM; - mod &= ~(MOD_BCLK_MASK << bfs_shift); + if (i2s->quirks & QUIRK_SUPPORTS_TDM) { + bfs_shift = EXYNOS5420_MOD_BCLK_SHIFT; + mod &= ~(EXYNOS5420_MOD_BCLK_MASK << bfs_shift); + } else { + bfs_shift = MOD_BCLK_SHIFT; + mod &= ~(MOD_BCLK_MASK << bfs_shift); + } + + /* Non-TDM I2S controllers do not support BCLK > 48 * FS */ + if (!tdm && bfs > 48) { + dev_err(&i2s->pdev->dev, "Unsupported BCLK divider\n"); + return; + } switch (bfs) { case 48: @@ -271,6 +305,21 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) case 16: mod |= (MOD_BCLK_16FS << bfs_shift); break; + case 64: + mod |= (EXYNOS5420_MOD_BCLK_64FS << bfs_shift); + break; + case 96: + mod |= (EXYNOS5420_MOD_BCLK_96FS << bfs_shift); + break; + case 128: + mod |= (EXYNOS5420_MOD_BCLK_128FS << bfs_shift); + break; + case 192: + mod |= (EXYNOS5420_MOD_BCLK_192FS << bfs_shift); + break; + case 256: + mod |= (EXYNOS5420_MOD_BCLK_256FS << bfs_shift); + break; default: dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n"); return; @@ -496,10 +545,17 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, { struct i2s_dai *i2s = to_info(dai); u32 mod = readl(i2s->addr + I2SMOD); - int lrp_shift = MOD_LRP_SHIFT, sdf_shift = MOD_SDF_SHIFT; - int sdf_mask, lrp_rlow; + int lrp_shift, sdf_shift, sdf_mask, lrp_rlow; u32 tmp = 0; + if (i2s->quirks & QUIRK_SUPPORTS_TDM) { + lrp_shift = EXYNOS5420_MOD_LRP_SHIFT; + sdf_shift = EXYNOS5420_MOD_SDF_SHIFT; + } else { + lrp_shift = MOD_LRP_SHIFT; + sdf_shift = MOD_SDF_SHIFT; + } + sdf_mask = MOD_SDF_MASK << sdf_shift; lrp_rlow = MOD_LR_RLOW << lrp_shift; @@ -1253,6 +1309,12 @@ static const struct samsung_i2s_dai_data i2sv5_dai_type = { .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR, }; +static const struct samsung_i2s_dai_data i2sv6_dai_type = { + .dai_type = TYPE_PRI, + .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | + QUIRK_SUPPORTS_TDM, +}; + static const struct samsung_i2s_dai_data samsung_dai_type_pri = { .dai_type = TYPE_PRI, }; @@ -1281,6 +1343,9 @@ static const struct of_device_id exynos_i2s_match[] = { }, { .compatible = "samsung,s5pv210-i2s", .data = &i2sv5_dai_type, + }, { + .compatible = "samsung,exynos5420-i2s", + .data = &i2sv6_dai_type, }, {}, }; -- cgit v1.2.3-71-gd317 From 0c1836a6563decc6de8622b2ed71523b3bdceb65 Mon Sep 17 00:00:00 2001 From: Amit Daniel Kachhap Date: Mon, 24 Jun 2013 16:20:27 +0530 Subject: thermal: exynos: Move exynos_thermal.h from include/* to driver/* folder This patch renames and moves include/linux/platform_data/exynos_thermal.h to drivers/thermal/samsung/exynos_tmu.h. This file movement is needed as exynos SOC's are not supporting non-DT based platforms and this file now just contains exynos tmu driver related definations. Also struct freq_clip_table is now moved to exynos_thermal_common.c as it fixes the compilation issue occuring because now this new tmu header file is included in tmu driver c file and not in the common thermal header file. Acked-by: Kukjin Kim Acked-by: Jonghwa Lee Acked-by: Eduardo Valentin Signed-off-by: Amit Daniel Kachhap Signed-off-by: Eduardo Valentin --- drivers/thermal/samsung/exynos_thermal_common.c | 1 - drivers/thermal/samsung/exynos_thermal_common.h | 16 ++++ drivers/thermal/samsung/exynos_tmu.c | 2 +- drivers/thermal/samsung/exynos_tmu.h | 107 +++++++++++++++++++++ include/linux/platform_data/exynos_thermal.h | 119 ------------------------ 5 files changed, 124 insertions(+), 121 deletions(-) create mode 100644 drivers/thermal/samsung/exynos_tmu.h delete mode 100644 include/linux/platform_data/exynos_thermal.h (limited to 'include') diff --git a/drivers/thermal/samsung/exynos_thermal_common.c b/drivers/thermal/samsung/exynos_thermal_common.c index f20f458be0a0..c9edc307dcf3 100644 --- a/drivers/thermal/samsung/exynos_thermal_common.c +++ b/drivers/thermal/samsung/exynos_thermal_common.c @@ -22,7 +22,6 @@ #include #include -#include #include #include diff --git a/drivers/thermal/samsung/exynos_thermal_common.h b/drivers/thermal/samsung/exynos_thermal_common.h index 8df18486078c..57ce33b39b54 100644 --- a/drivers/thermal/samsung/exynos_thermal_common.h +++ b/drivers/thermal/samsung/exynos_thermal_common.h @@ -44,6 +44,22 @@ #define EXYNOS_ZONE_COUNT 3 +/** + * struct freq_clip_table + * @freq_clip_max: maximum frequency allowed for this cooling state. + * @temp_level: Temperature level at which the temperature clipping will + * happen. + * @mask_val: cpumask of the allowed cpu's where the clipping will take place. + * + * This structure is required to be filled and passed to the + * cpufreq_cooling_unregister function. + */ +struct freq_clip_table { + unsigned int freq_clip_max; + unsigned int temp_level; + const struct cpumask *mask_val; +}; + struct thermal_trip_point_conf { int trip_val[MAX_TRIP_COUNT]; int trip_count; diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 2b2cc333cd7d..568c31d0d1cb 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -27,9 +27,9 @@ #include #include #include -#include #include "exynos_thermal_common.h" +#include "exynos_tmu.h" /* Exynos generic registers */ #define EXYNOS_TMU_REG_TRIMINFO 0x0 diff --git a/drivers/thermal/samsung/exynos_tmu.h b/drivers/thermal/samsung/exynos_tmu.h new file mode 100644 index 000000000000..1857609518f2 --- /dev/null +++ b/drivers/thermal/samsung/exynos_tmu.h @@ -0,0 +1,107 @@ +/* + * exynos_tmu.h - Samsung EXYNOS TMU (Thermal Management Unit) + * + * Copyright (C) 2011 Samsung Electronics + * Donggeun Kim + * Amit Daniel Kachhap + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _EXYNOS_TMU_H +#define _EXYNOS_TMU_H +#include + +#include "exynos_thermal_common.h" + +enum calibration_type { + TYPE_ONE_POINT_TRIMMING, + TYPE_TWO_POINT_TRIMMING, + TYPE_NONE, +}; + +enum soc_type { + SOC_ARCH_EXYNOS4210 = 1, + SOC_ARCH_EXYNOS, +}; + +/** + * struct exynos_tmu_platform_data + * @threshold: basic temperature for generating interrupt + * 25 <= threshold <= 125 [unit: degree Celsius] + * @threshold_falling: differntial value for setting threshold + * of temperature falling interrupt. + * @trigger_levels: array for each interrupt levels + * [unit: degree Celsius] + * 0: temperature for trigger_level0 interrupt + * condition for trigger_level0 interrupt: + * current temperature > threshold + trigger_levels[0] + * 1: temperature for trigger_level1 interrupt + * condition for trigger_level1 interrupt: + * current temperature > threshold + trigger_levels[1] + * 2: temperature for trigger_level2 interrupt + * condition for trigger_level2 interrupt: + * current temperature > threshold + trigger_levels[2] + * 3: temperature for trigger_level3 interrupt + * condition for trigger_level3 interrupt: + * current temperature > threshold + trigger_levels[3] + * @trigger_level0_en: + * 1 = enable trigger_level0 interrupt, + * 0 = disable trigger_level0 interrupt + * @trigger_level1_en: + * 1 = enable trigger_level1 interrupt, + * 0 = disable trigger_level1 interrupt + * @trigger_level2_en: + * 1 = enable trigger_level2 interrupt, + * 0 = disable trigger_level2 interrupt + * @trigger_level3_en: + * 1 = enable trigger_level3 interrupt, + * 0 = disable trigger_level3 interrupt + * @gain: gain of amplifier in the positive-TC generator block + * 0 <= gain <= 15 + * @reference_voltage: reference voltage of amplifier + * in the positive-TC generator block + * 0 <= reference_voltage <= 31 + * @noise_cancel_mode: noise cancellation mode + * 000, 100, 101, 110 and 111 can be different modes + * @type: determines the type of SOC + * @efuse_value: platform defined fuse value + * @cal_type: calibration type for temperature + * @freq_clip_table: Table representing frequency reduction percentage. + * @freq_tab_count: Count of the above table as frequency reduction may + * applicable to only some of the trigger levels. + * + * This structure is required for configuration of exynos_tmu driver. + */ +struct exynos_tmu_platform_data { + u8 threshold; + u8 threshold_falling; + u8 trigger_levels[4]; + bool trigger_level0_en; + bool trigger_level1_en; + bool trigger_level2_en; + bool trigger_level3_en; + + u8 gain; + u8 reference_voltage; + u8 noise_cancel_mode; + u32 efuse_value; + + enum calibration_type cal_type; + enum soc_type type; + struct freq_clip_table freq_tab[4]; + unsigned int freq_tab_count; +}; +#endif /* _EXYNOS_TMU_H */ diff --git a/include/linux/platform_data/exynos_thermal.h b/include/linux/platform_data/exynos_thermal.h deleted file mode 100644 index da7e6274b175..000000000000 --- a/include/linux/platform_data/exynos_thermal.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * exynos_thermal.h - Samsung EXYNOS TMU (Thermal Management Unit) - * - * Copyright (C) 2011 Samsung Electronics - * Donggeun Kim - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _LINUX_EXYNOS_THERMAL_H -#define _LINUX_EXYNOS_THERMAL_H -#include - -enum calibration_type { - TYPE_ONE_POINT_TRIMMING, - TYPE_TWO_POINT_TRIMMING, - TYPE_NONE, -}; - -enum soc_type { - SOC_ARCH_EXYNOS4210 = 1, - SOC_ARCH_EXYNOS, -}; -/** - * struct freq_clip_table - * @freq_clip_max: maximum frequency allowed for this cooling state. - * @temp_level: Temperature level at which the temperature clipping will - * happen. - * @mask_val: cpumask of the allowed cpu's where the clipping will take place. - * - * This structure is required to be filled and passed to the - * cpufreq_cooling_unregister function. - */ -struct freq_clip_table { - unsigned int freq_clip_max; - unsigned int temp_level; - const struct cpumask *mask_val; -}; - -/** - * struct exynos_tmu_platform_data - * @threshold: basic temperature for generating interrupt - * 25 <= threshold <= 125 [unit: degree Celsius] - * @threshold_falling: differntial value for setting threshold - * of temperature falling interrupt. - * @trigger_levels: array for each interrupt levels - * [unit: degree Celsius] - * 0: temperature for trigger_level0 interrupt - * condition for trigger_level0 interrupt: - * current temperature > threshold + trigger_levels[0] - * 1: temperature for trigger_level1 interrupt - * condition for trigger_level1 interrupt: - * current temperature > threshold + trigger_levels[1] - * 2: temperature for trigger_level2 interrupt - * condition for trigger_level2 interrupt: - * current temperature > threshold + trigger_levels[2] - * 3: temperature for trigger_level3 interrupt - * condition for trigger_level3 interrupt: - * current temperature > threshold + trigger_levels[3] - * @trigger_level0_en: - * 1 = enable trigger_level0 interrupt, - * 0 = disable trigger_level0 interrupt - * @trigger_level1_en: - * 1 = enable trigger_level1 interrupt, - * 0 = disable trigger_level1 interrupt - * @trigger_level2_en: - * 1 = enable trigger_level2 interrupt, - * 0 = disable trigger_level2 interrupt - * @trigger_level3_en: - * 1 = enable trigger_level3 interrupt, - * 0 = disable trigger_level3 interrupt - * @gain: gain of amplifier in the positive-TC generator block - * 0 <= gain <= 15 - * @reference_voltage: reference voltage of amplifier - * in the positive-TC generator block - * 0 <= reference_voltage <= 31 - * @noise_cancel_mode: noise cancellation mode - * 000, 100, 101, 110 and 111 can be different modes - * @type: determines the type of SOC - * @efuse_value: platform defined fuse value - * @cal_type: calibration type for temperature - * @freq_clip_table: Table representing frequency reduction percentage. - * @freq_tab_count: Count of the above table as frequency reduction may - * applicable to only some of the trigger levels. - * - * This structure is required for configuration of exynos_tmu driver. - */ -struct exynos_tmu_platform_data { - u8 threshold; - u8 threshold_falling; - u8 trigger_levels[4]; - bool trigger_level0_en; - bool trigger_level1_en; - bool trigger_level2_en; - bool trigger_level3_en; - - u8 gain; - u8 reference_voltage; - u8 noise_cancel_mode; - u32 efuse_value; - - enum calibration_type cal_type; - enum soc_type type; - struct freq_clip_table freq_tab[4]; - unsigned int freq_tab_count; -}; -#endif /* _LINUX_EXYNOS_THERMAL_H */ -- cgit v1.2.3-71-gd317 From bd0779370588386e4a67ba5d0b176cfded8e6a53 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 7 Aug 2013 18:13:20 +0200 Subject: netfilter: nfnetlink_queue: allow to attach expectations to conntracks This patch adds the capability to attach expectations via nfnetlink_queue. This is required by conntrack helpers that trigger expectations based on the first packet seen like the TFTP and the DHCPv6 user-space helpers. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 2 + include/net/netfilter/nfnetlink_queue.h | 8 +++ include/uapi/linux/netfilter/nfnetlink_queue.h | 1 + net/netfilter/nf_conntrack_netlink.c | 95 ++++++++++++++++++++++---- net/netfilter/nfnetlink_queue_core.c | 9 ++- net/netfilter/nfnetlink_queue_ct.c | 15 ++++ 6 files changed, 114 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 655d5d198d49..e2cf786be22f 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -325,6 +325,8 @@ struct nfq_ct_hook { size_t (*build_size)(const struct nf_conn *ct); int (*build)(struct sk_buff *skb, struct nf_conn *ct); int (*parse)(const struct nlattr *attr, struct nf_conn *ct); + int (*attach_expect)(const struct nlattr *attr, struct nf_conn *ct, + u32 portid, u32 report); }; extern struct nfq_ct_hook __rcu *nfq_ct_hook; diff --git a/include/net/netfilter/nfnetlink_queue.h b/include/net/netfilter/nfnetlink_queue.h index 86267a529514..aff88ba91391 100644 --- a/include/net/netfilter/nfnetlink_queue.h +++ b/include/net/netfilter/nfnetlink_queue.h @@ -15,6 +15,8 @@ int nfqnl_ct_put(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo); void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, int diff); +int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, + u32 portid, u32 report); #else inline struct nf_conn * nfqnl_ct_get(struct sk_buff *entskb, size_t *size, enum ip_conntrack_info *ctinfo) @@ -39,5 +41,11 @@ inline void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, int diff) { } + +inline int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, + u32 portid, u32 report) +{ + return 0; +} #endif /* NF_CONNTRACK */ #endif diff --git a/include/uapi/linux/netfilter/nfnetlink_queue.h b/include/uapi/linux/netfilter/nfnetlink_queue.h index 3a9b92147339..0132bad79de7 100644 --- a/include/uapi/linux/netfilter/nfnetlink_queue.h +++ b/include/uapi/linux/netfilter/nfnetlink_queue.h @@ -46,6 +46,7 @@ enum nfqnl_attr_type { NFQA_CT_INFO, /* enum ip_conntrack_info */ NFQA_CAP_LEN, /* __u32 length of captured packet */ NFQA_SKB_INFO, /* __u32 skb meta information */ + NFQA_EXP, /* nf_conntrack_netlink.h */ __NFQA_MAX }; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 9aaa68bbbcdb..fa61fea63234 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1987,6 +1987,27 @@ out: return err == -EAGAIN ? -ENOBUFS : err; } +static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { + [CTA_EXPECT_MASTER] = { .type = NLA_NESTED }, + [CTA_EXPECT_TUPLE] = { .type = NLA_NESTED }, + [CTA_EXPECT_MASK] = { .type = NLA_NESTED }, + [CTA_EXPECT_TIMEOUT] = { .type = NLA_U32 }, + [CTA_EXPECT_ID] = { .type = NLA_U32 }, + [CTA_EXPECT_HELP_NAME] = { .type = NLA_NUL_STRING, + .len = NF_CT_HELPER_NAME_LEN - 1 }, + [CTA_EXPECT_ZONE] = { .type = NLA_U16 }, + [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, + [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, + [CTA_EXPECT_NAT] = { .type = NLA_NESTED }, + [CTA_EXPECT_FN] = { .type = NLA_NUL_STRING }, +}; + +static struct nf_conntrack_expect * +ctnetlink_alloc_expect(const struct nlattr *const cda[], struct nf_conn *ct, + struct nf_conntrack_helper *helper, + struct nf_conntrack_tuple *tuple, + struct nf_conntrack_tuple *mask); + #ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT static size_t ctnetlink_nfqueue_build_size(const struct nf_conn *ct) @@ -2127,10 +2148,69 @@ ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct) return ret; } +static int ctnetlink_nfqueue_exp_parse(const struct nlattr * const *cda, + const struct nf_conn *ct, + struct nf_conntrack_tuple *tuple, + struct nf_conntrack_tuple *mask) +{ + int err; + + err = ctnetlink_parse_tuple(cda, tuple, CTA_EXPECT_TUPLE, + nf_ct_l3num(ct)); + if (err < 0) + return err; + + return ctnetlink_parse_tuple(cda, mask, CTA_EXPECT_MASK, + nf_ct_l3num(ct)); +} + +static int +ctnetlink_nfqueue_attach_expect(const struct nlattr *attr, struct nf_conn *ct, + u32 portid, u32 report) +{ + struct nlattr *cda[CTA_EXPECT_MAX+1]; + struct nf_conntrack_tuple tuple, mask; + struct nf_conntrack_helper *helper; + struct nf_conntrack_expect *exp; + int err; + + err = nla_parse_nested(cda, CTA_EXPECT_MAX, attr, exp_nla_policy); + if (err < 0) + return err; + + err = ctnetlink_nfqueue_exp_parse((const struct nlattr * const *)cda, + ct, &tuple, &mask); + if (err < 0) + return err; + + if (cda[CTA_EXPECT_HELP_NAME]) { + const char *helpname = nla_data(cda[CTA_EXPECT_HELP_NAME]); + + helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct), + nf_ct_protonum(ct)); + if (helper == NULL) + return -EOPNOTSUPP; + } + + exp = ctnetlink_alloc_expect((const struct nlattr * const *)cda, ct, + helper, &tuple, &mask); + if (IS_ERR(exp)) + return PTR_ERR(exp); + + err = nf_ct_expect_related_report(exp, portid, report); + if (err < 0) { + nf_ct_expect_put(exp); + return err; + } + + return 0; +} + static struct nfq_ct_hook ctnetlink_nfqueue_hook = { .build_size = ctnetlink_nfqueue_build_size, .build = ctnetlink_nfqueue_build, .parse = ctnetlink_nfqueue_parse, + .attach_expect = ctnetlink_nfqueue_attach_expect, }; #endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ @@ -2498,21 +2578,6 @@ static int ctnetlink_dump_exp_ct(struct sock *ctnl, struct sk_buff *skb, return err; } -static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = { - [CTA_EXPECT_MASTER] = { .type = NLA_NESTED }, - [CTA_EXPECT_TUPLE] = { .type = NLA_NESTED }, - [CTA_EXPECT_MASK] = { .type = NLA_NESTED }, - [CTA_EXPECT_TIMEOUT] = { .type = NLA_U32 }, - [CTA_EXPECT_ID] = { .type = NLA_U32 }, - [CTA_EXPECT_HELP_NAME] = { .type = NLA_NUL_STRING, - .len = NF_CT_HELPER_NAME_LEN - 1 }, - [CTA_EXPECT_ZONE] = { .type = NLA_U16 }, - [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, - [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, - [CTA_EXPECT_NAT] = { .type = NLA_NESTED }, - [CTA_EXPECT_FN] = { .type = NLA_NUL_STRING }, -}; - static int ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index ec9de12aa488..e8c9f3bb779c 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -859,6 +859,7 @@ static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = { [NFQA_MARK] = { .type = NLA_U32 }, [NFQA_PAYLOAD] = { .type = NLA_UNSPEC }, [NFQA_CT] = { .type = NLA_UNSPEC }, + [NFQA_EXP] = { .type = NLA_UNSPEC }, }; static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { @@ -987,8 +988,14 @@ nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, if (entry == NULL) return -ENOENT; - if (nfqa[NFQA_CT]) + if (nfqa[NFQA_CT]) { ct = nfqnl_ct_parse(entry->skb, nfqa[NFQA_CT], &ctinfo); + if (ct && nfqa[NFQA_EXP]) { + nfqnl_attach_expect(ct, nfqa[NFQA_EXP], + NETLINK_CB(skb).portid, + nlmsg_report(nlh)); + } + } if (nfqa[NFQA_PAYLOAD]) { u16 payload_len = nla_len(nfqa[NFQA_PAYLOAD]); diff --git a/net/netfilter/nfnetlink_queue_ct.c b/net/netfilter/nfnetlink_queue_ct.c index ab61d66bc0b9..be893039966d 100644 --- a/net/netfilter/nfnetlink_queue_ct.c +++ b/net/netfilter/nfnetlink_queue_ct.c @@ -96,3 +96,18 @@ void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, if ((ct->status & IPS_NAT_MASK) && diff) nfq_nat_ct->seq_adjust(skb, ct, ctinfo, diff); } + +int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, + u32 portid, u32 report) +{ + struct nfq_ct_hook *nfq_ct; + + if (nf_ct_is_untracked(ct)) + return 0; + + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct == NULL) + return -EOPNOTSUPP; + + return nfq_ct->attach_expect(attr, ct, portid, report); +} -- cgit v1.2.3-71-gd317 From 35ef10da65d43211f4cd7e7822cbb3becdfc0ae1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 13 Aug 2013 11:01:54 -0400 Subject: cgroup: rename cgroup_subsys_state->dput_work and its callback function css (cgroup_subsys_state) will become RCU protected and there will be two stages which require punting to work item during release. To prepare for using the work item for multiple times, rename css->dput_work to css->destroy_work and css_dput_fn() to css_free_work_fn() and move work item initialization from css init to right before the actual usage. This reorganization doesn't introduce any behavior change. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 2 +- kernel/cgroup.c | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 8ec5b0f38292..12d66fee26f8 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -80,7 +80,7 @@ struct cgroup_subsys_state { struct css_id __rcu *id; /* Used to put @cgroup->dentry on the last css_put() */ - struct work_struct dput_work; + struct work_struct destroy_work; }; /* bits in struct cgroup_subsys_state flags field */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 49ad96ee08e1..0b280978f097 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4259,10 +4259,10 @@ err: return ret; } -static void css_dput_fn(struct work_struct *work) +static void css_free_work_fn(struct work_struct *work) { struct cgroup_subsys_state *css = - container_of(work, struct cgroup_subsys_state, dput_work); + container_of(work, struct cgroup_subsys_state, destroy_work); cgroup_dput(css->cgroup); } @@ -4272,7 +4272,14 @@ static void css_release(struct percpu_ref *ref) struct cgroup_subsys_state *css = container_of(ref, struct cgroup_subsys_state, refcnt); - schedule_work(&css->dput_work); + /* + * css holds an extra ref to @cgrp->dentry which is put on the last + * css_put(). dput() requires process context, which css_put() may + * be called without. @css->destroy_work will be used to invoke + * dput() asynchronously from css_put(). + */ + INIT_WORK(&css->destroy_work, css_free_work_fn); + schedule_work(&css->destroy_work); } static void init_cgroup_css(struct cgroup_subsys_state *css, @@ -4287,14 +4294,6 @@ static void init_cgroup_css(struct cgroup_subsys_state *css, css->flags |= CSS_ROOT; BUG_ON(cgroup_css(cgrp, ss->subsys_id)); cgrp->subsys[ss->subsys_id] = css; - - /* - * css holds an extra ref to @cgrp->dentry which is put on the last - * css_put(). dput() requires process context, which css_put() may - * be called without. @css->dput_work will be used to invoke - * dput() asynchronously from css_put(). - */ - INIT_WORK(&css->dput_work, css_dput_fn); } /* invoke ->css_online() on a new CSS and mark it online if successful */ -- cgit v1.2.3-71-gd317 From 0ae78e0bf10ac38ab53548e18383afc9997eca22 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 13 Aug 2013 11:01:54 -0400 Subject: cgroup: add cgroup_subsys_state->parent With the planned unified hierarchy, css's (cgroup_subsys_state) will be RCU protected and allowed to be attached and detached dynamically over the course of a cgroup's lifetime. This means that css's will stay accessible after being detached from its cgroup - the matching pointer in cgroup->subsys[] cleared - for ref draining and RCU grace period. cgroup core still wants to guarantee that the parent css is never destroyed before its children and css_parent() always returns the parent regardless of the state of the child css as long as it's accessible. This patch makes css's hold onto their parents and adds css->parent so that the parent css is never detroyed before its children and can be determined without consulting the cgroups. cgroup->dummy_css is also updated to point to the parent dummy_css; however, it doesn't need to worry about object lifetime as the parent cgroup is already pinned by the child. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 13 ++++--------- kernel/cgroup.c | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 12d66fee26f8..8a5dc91fbaad 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -75,6 +75,9 @@ struct cgroup_subsys_state { /* reference count - access via css_[try]get() and css_put() */ struct percpu_ref refcnt; + /* the parent css */ + struct cgroup_subsys_state *parent; + unsigned long flags; /* ID for this css, if possible */ struct css_id __rcu *id; @@ -666,15 +669,7 @@ struct cgroup_subsys { static inline struct cgroup_subsys_state *css_parent(struct cgroup_subsys_state *css) { - struct cgroup *parent_cgrp = css->cgroup->parent; - - if (!parent_cgrp) - return NULL; - - if (css->ss) - return parent_cgrp->subsys[css->ss->subsys_id]; - else - return &parent_cgrp->dummy_css; + return css->parent; } /** diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 0b280978f097..5c6dd7ed26a7 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4264,6 +4264,9 @@ static void css_free_work_fn(struct work_struct *work) struct cgroup_subsys_state *css = container_of(work, struct cgroup_subsys_state, destroy_work); + if (css->parent) + css_put(css->parent); + cgroup_dput(css->cgroup); } @@ -4290,8 +4293,12 @@ static void init_cgroup_css(struct cgroup_subsys_state *css, css->ss = ss; css->flags = 0; css->id = NULL; - if (cgrp == cgroup_dummy_top) + + if (cgrp->parent) + css->parent = cgroup_css(cgrp->parent, ss->subsys_id); + else css->flags |= CSS_ROOT; + BUG_ON(cgroup_css(cgrp, ss->subsys_id)); cgrp->subsys[ss->subsys_id] = css; } @@ -4388,6 +4395,7 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, cgrp->dentry = dentry; cgrp->parent = parent; + cgrp->dummy_css.parent = &parent->dummy_css; cgrp->root = parent->root; if (notify_on_release(parent)) @@ -4436,9 +4444,13 @@ static long cgroup_create(struct cgroup *parent, struct dentry *dentry, list_add_tail_rcu(&cgrp->sibling, &cgrp->parent->children); root->number_of_cgroups++; - /* each css holds a ref to the cgroup's dentry */ - for_each_root_subsys(root, ss) + /* each css holds a ref to the cgroup's dentry and the parent css */ + for_each_root_subsys(root, ss) { + struct cgroup_subsys_state *css = cgroup_css(cgrp, ss->subsys_id); + dget(dentry); + percpu_ref_get(&css->parent->refcnt); + } /* hold a ref to the parent's dentry */ dget(parent->dentry); -- cgit v1.2.3-71-gd317 From 73e80ed8007fc48a6deeb295ba37159fad274bd2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 13 Aug 2013 11:01:55 -0400 Subject: cgroup: add __rcu modifier to cgroup->subsys[] For the planned unified hierarchy, each css (cgroup_subsys_state) will be RCU protected so that it can be created and destroyed individually while allowing RCU accesses. Previous changes ensured that all cgroup->subsys[] accesses use the cgroup_css() accessor. This patch adds __rcu modifier to cgroup->subsys[], add matching RCU dereference in cgroup_css() and convert all assignments to either rcu_assign_pointer() or RCU_INIT_POINTER(). This change prepares for the actual RCUfication of css's and doesn't introduce any visible behavior change. The conversion is verified with sparse and all accesses are properly RCU annotated. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 2 +- kernel/cgroup.c | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 8a5dc91fbaad..eb200b5794e7 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -204,7 +204,7 @@ struct cgroup { struct cgroup_name __rcu *name; /* Private pointers for each registered subsystem */ - struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT]; + struct cgroup_subsys_state __rcu *subsys[CGROUP_SUBSYS_COUNT]; struct cgroupfs_root *root; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index d63beffd41e1..c27101622567 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -229,11 +229,16 @@ static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], * @subsys_id: the subsystem of interest * * Return @cgrp's css (cgroup_subsys_state) associated with @subsys_id. + * This function must be called either under cgroup_mutex or + * rcu_read_lock() and the caller is responsible for pinning the returned + * css if it wants to keep accessing it outside the said locks. This + * function may return %NULL if @cgrp doesn't have @subsys_id enabled. */ static struct cgroup_subsys_state *cgroup_css(struct cgroup *cgrp, int subsys_id) { - return cgrp->subsys[subsys_id]; + return rcu_dereference_check(cgrp->subsys[subsys_id], + lockdep_is_held(&cgroup_mutex)); } /* convenient tests for these bits */ @@ -1072,8 +1077,10 @@ static int rebind_subsystems(struct cgroupfs_root *root, BUG_ON(!cgroup_css(cgroup_dummy_top, i)); BUG_ON(cgroup_css(cgroup_dummy_top, i)->cgroup != cgroup_dummy_top); - cgrp->subsys[i] = cgroup_dummy_top->subsys[i]; + rcu_assign_pointer(cgrp->subsys[i], + cgroup_css(cgroup_dummy_top, i)); cgroup_css(cgrp, i)->cgroup = cgrp; + list_move(&ss->sibling, &root->subsys_list); ss->root = root; if (ss->bind) @@ -1088,8 +1095,10 @@ static int rebind_subsystems(struct cgroupfs_root *root, if (ss->bind) ss->bind(cgroup_css(cgroup_dummy_top, i)); + cgroup_css(cgroup_dummy_top, i)->cgroup = cgroup_dummy_top; - cgrp->subsys[i] = NULL; + RCU_INIT_POINTER(cgrp->subsys[i], NULL); + cgroup_subsys[i]->root = &cgroup_dummy_root; list_move(&ss->sibling, &cgroup_dummy_root.subsys_list); @@ -4314,7 +4323,7 @@ static void init_cgroup_css(struct cgroup_subsys_state *css, css->flags |= CSS_ROOT; BUG_ON(cgroup_css(cgrp, ss->subsys_id)); - cgrp->subsys[ss->subsys_id] = css; + rcu_assign_pointer(cgrp->subsys[ss->subsys_id], css); } /* invoke ->css_online() on a new CSS and mark it online if successful */ @@ -4962,7 +4971,7 @@ void cgroup_unload_subsys(struct cgroup_subsys *ss) * also takes care of freeing the css_id. */ ss->css_free(cgroup_css(cgroup_dummy_top, ss->subsys_id)); - cgroup_dummy_top->subsys[ss->subsys_id] = NULL; + RCU_INIT_POINTER(cgroup_dummy_top->subsys[ss->subsys_id], NULL); mutex_unlock(&cgroup_mutex); } -- cgit v1.2.3-71-gd317 From b4f173752a56187bd55752b0474429202f2ab1d3 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 6 May 2013 14:19:19 -0600 Subject: ARM: tegra: disable LP2 cpuidle state if PCIe is enabled Tegra20 HW appears to have a bug such that PCIe device interrupts, whether they are legacy IRQs or MSI, are lost when LP2 is enabled. To work around this, simply disable LP2 if any PCIe devices with interrupts are present. Detect this via the IRQ domain map operation. This is slightly over-conservative; if a device with an interrupt is present but the driver does not actually use them, LP2 will still be disabled. However, this is a reasonable trade-off which enables a simpler workaround. Signed-off-by: Stephen Warren Tested-by: Thierry Reding Acked-by: Thierry Reding --- arch/arm/mach-tegra/cpuidle-tegra20.c | 12 ++++++++++++ arch/arm/mach-tegra/cpuidle.c | 10 ++++++++++ arch/arm/mach-tegra/cpuidle.h | 1 + drivers/pci/host/pci-tegra.c | 5 +++++ include/linux/tegra-cpuidle.h | 19 +++++++++++++++++++ 5 files changed, 47 insertions(+) create mode 100644 include/linux/tegra-cpuidle.h (limited to 'include') diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c index 706aa4215c36..b82dcaee2ef4 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra20.c +++ b/arch/arm/mach-tegra/cpuidle-tegra20.c @@ -211,6 +211,18 @@ static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, } #endif +/* + * Tegra20 HW appears to have a bug such that PCIe device interrupts, whether + * they are legacy IRQs or MSI, are lost when LP2 is enabled. To work around + * this, simply disable LP2 if the PCI driver and DT node are both enabled. + */ +void tegra20_cpuidle_pcie_irqs_in_use(void) +{ + pr_info_once( + "Disabling cpuidle LP2 state, since PCIe IRQs are in use\n"); + tegra_idle_driver.states[1].disabled = true; +} + int __init tegra20_cpuidle_init(void) { return cpuidle_register(&tegra_idle_driver, cpu_possible_mask); diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c index e85973cef037..0961dfcf83a4 100644 --- a/arch/arm/mach-tegra/cpuidle.c +++ b/arch/arm/mach-tegra/cpuidle.c @@ -44,3 +44,13 @@ void __init tegra_cpuidle_init(void) break; } } + +void tegra_cpuidle_pcie_irqs_in_use(void) +{ + switch (tegra_chip_id) { + case TEGRA20: + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) + tegra20_cpuidle_pcie_irqs_in_use(); + break; + } +} diff --git a/arch/arm/mach-tegra/cpuidle.h b/arch/arm/mach-tegra/cpuidle.h index 9ec2c1ab0fa4..c017dab60ffa 100644 --- a/arch/arm/mach-tegra/cpuidle.h +++ b/arch/arm/mach-tegra/cpuidle.h @@ -19,6 +19,7 @@ #ifdef CONFIG_CPU_IDLE int tegra20_cpuidle_init(void); +void tegra20_cpuidle_pcie_irqs_in_use(void); int tegra30_cpuidle_init(void); int tegra114_cpuidle_init(void); void tegra_cpuidle_init(void); diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index ad95c406a6d0..7356741de36b 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -636,6 +637,8 @@ static int tegra_pcie_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin) { struct tegra_pcie *pcie = sys_to_pcie(pdev->bus->sysdata); + tegra_cpuidle_pcie_irqs_in_use(); + return pcie->irq; } @@ -1221,6 +1224,8 @@ static int tegra_msi_map(struct irq_domain *domain, unsigned int irq, irq_set_chip_data(irq, domain->host_data); set_irq_flags(irq, IRQF_VALID); + tegra_cpuidle_pcie_irqs_in_use(); + return 0; } diff --git a/include/linux/tegra-cpuidle.h b/include/linux/tegra-cpuidle.h new file mode 100644 index 000000000000..dda3647242a4 --- /dev/null +++ b/include/linux/tegra-cpuidle.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef __LINUX_TEGRA_CPUIDLE_H__ +#define __LINUX_TEGRA_CPUIDLE_H__ + +void tegra_cpuidle_pcie_irqs_in_use(void); + +#endif -- cgit v1.2.3-71-gd317 From 73c40c616a33fcb7961b3c90a91b550813129b3e Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Thu, 1 Aug 2013 18:49:53 +0300 Subject: IB/core: Add locking around event dispatching on XRC target QPs Fix a potential race when event occurrs on a target XRC QP and in the middle of reporting that on its shared qps, one of them is destroyed by user space application. Also add note for kernel consumers in ib_verbs.h that they must not destroy the QP from within the handler. Signed-off-by: Yishai Hadas Signed-off-by: Jack Morgenstein Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/core/verbs.c | 3 +++ include/rdma/ib_verbs.h | 6 ++++++ 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 22192deb8828..077fd641b300 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -346,10 +346,13 @@ EXPORT_SYMBOL(ib_destroy_srq); static void __ib_shared_qp_event_handler(struct ib_event *event, void *context) { struct ib_qp *qp = context; + unsigned long flags; + spin_lock_irqsave(&qp->device->event_handler_lock, flags); list_for_each_entry(event->element.qp, &qp->open_list, open_list) if (event->element.qp->event_handler) event->element.qp->event_handler(event, event->element.qp->qp_context); + spin_unlock_irqrestore(&qp->device->event_handler_lock, flags); } static void __ib_insert_xrcd_qp(struct ib_xrcd *xrcd, struct ib_qp *qp) diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 645c3cedce9c..a84d3dfc4076 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -635,6 +635,12 @@ enum ib_qp_create_flags { IB_QP_CREATE_RESERVED_END = 1 << 31, }; + +/* + * Note: users may not call ib_close_qp or ib_destroy_qp from the event_handler + * callback to destroy the passed in QP. + */ + struct ib_qp_init_attr { void (*event_handler)(struct ib_event *, void *); void *qp_context; -- cgit v1.2.3-71-gd317 From ebd8b934e23f45ad3fc8a5a28bc5a96741a6a106 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Sat, 10 Aug 2013 15:22:58 -0700 Subject: pptp: fix byte order warnings Pptp driver has lots of byte order warnings from sparse. This was because the on-the-wire header is in network byte order (obviously) but the definition did not reflect that. Also, the address structure to user space actually put the call id in host order. Rather than break ABI compatibility, just acknowledge the existing design. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/ppp/pptp.c | 10 +++++----- include/uapi/linux/if_pppox.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index 9785a3ac02b3..6fa5ae00039f 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -83,11 +83,11 @@ static const struct proto_ops pptp_ops; struct pptp_gre_header { u8 flags; u8 ver; - u16 protocol; - u16 payload_len; - u16 call_id; - u32 seq; - u32 ack; + __be16 protocol; + __be16 payload_len; + __be16 call_id; + __be32 seq; + __be32 ack; } __packed; static struct pppox_sock *lookup_chan(u16 call_id, __be32 s_addr) diff --git a/include/uapi/linux/if_pppox.h b/include/uapi/linux/if_pppox.h index e36a4aecd311..e128769331b5 100644 --- a/include/uapi/linux/if_pppox.h +++ b/include/uapi/linux/if_pppox.h @@ -46,7 +46,7 @@ struct pppoe_addr { * PPTP addressing definition */ struct pptp_addr { - __be16 call_id; + __u16 call_id; struct in_addr sin_addr; }; -- cgit v1.2.3-71-gd317 From 1972b5b3a66f1b6a9df04cc91faf401354919262 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 25 Jun 2013 12:42:54 +0200 Subject: NFC: Document secure element addition/removal netlink events Signed-off-by: Samuel Ortiz --- include/uapi/linux/nfc.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 8137dd8d2adf..40ada984cb78 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -71,6 +71,11 @@ * @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element. * @NFC_CMD_FW_DOWNLOAD: Request to Load/flash firmware, or event to inform * that some firmware was loaded + * @NFC_EVENT_SE_ADDED: Event emitted when a new secure element is discovered. + * This typically will be sent whenever a new NFC controller with either + * an embedded SE or an UICC one connected to it through SWP. + * @NFC_EVENT_SE_REMOVED: Event emitted when a secure element is removed from + * the system, as a consequence of e.g. an NFC controller being unplugged. */ enum nfc_commands { NFC_CMD_UNSPEC, -- cgit v1.2.3-71-gd317 From 91a32269e31282405db405b9ec9ce1a30568e4b0 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 25 Jun 2013 16:22:08 +0200 Subject: NFC: Define secure element connectivity and transaction events The SE_CONNECTIVITY event is for an SE to request connection to e.g. a modem. The SE_TRANSACTION one is sent when an application running on a specific SE wants to notify the host CPU about the end of a transaction. Those events respectively map to the EVT_CONNECTIVITY and the EVT_TRANSACTION HCI events. Signed-off-by: Samuel Ortiz --- include/uapi/linux/nfc.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 40ada984cb78..539d60494c04 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -76,6 +76,14 @@ * an embedded SE or an UICC one connected to it through SWP. * @NFC_EVENT_SE_REMOVED: Event emitted when a secure element is removed from * the system, as a consequence of e.g. an NFC controller being unplugged. + * @NFC_EVENT_SE_CONNECTIVITY: This event is emitted whenever a secure element + * is requesting connectivity access. For example a UICC SE may need to + * talk with a sleeping modem and will notify this need by sending this + * event. It is then up to userspace to decide if it will wake the modem + * up or not. + * @NFC_EVENT_SE_TRANSACTION: This event is sent when an application running on + * a specific SE notifies us about the end of a transaction. The parameter + * for this event is the application ID (AID). */ enum nfc_commands { NFC_CMD_UNSPEC, @@ -102,6 +110,8 @@ enum nfc_commands { NFC_CMD_FW_DOWNLOAD, NFC_EVENT_SE_ADDED, NFC_EVENT_SE_REMOVED, + NFC_EVENT_SE_CONNECTIVITY, + NFC_EVENT_SE_TRANSACTION, /* private: internal use only */ __NFC_CMD_AFTER_LAST }; @@ -159,6 +169,7 @@ enum nfc_attrs { NFC_ATTR_FIRMWARE_NAME, NFC_ATTR_SE_INDEX, NFC_ATTR_SE_TYPE, + NFC_ATTR_SE_AID, /* private: internal use only */ __NFC_ATTR_AFTER_LAST }; -- cgit v1.2.3-71-gd317 From ac22ac466a659f1b2e02a2e2ee23fc5c42da2c95 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 24 Jul 2013 18:10:50 +0200 Subject: NFC: Add a GET_SE netlink API In order to fetch the discovered secure elements from an NFC controller, we need to send a netlink command that will dump the list of available SEs from NFC. Signed-off-by: Samuel Ortiz --- include/uapi/linux/nfc.h | 2 ++ net/nfc/netlink.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 539d60494c04..029921b067fd 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -84,6 +84,7 @@ * @NFC_EVENT_SE_TRANSACTION: This event is sent when an application running on * a specific SE notifies us about the end of a transaction. The parameter * for this event is the application ID (AID). + * @NFC_CMD_GET_SE: Dump all discovered secure elements from an NFC controller. */ enum nfc_commands { NFC_CMD_UNSPEC, @@ -112,6 +113,7 @@ enum nfc_commands { NFC_EVENT_SE_REMOVED, NFC_EVENT_SE_CONNECTIVITY, NFC_EVENT_SE_TRANSACTION, + NFC_CMD_GET_SE, /* private: internal use only */ __NFC_CMD_AFTER_LAST }; diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index f16fd59d4160..3b08ef90e045 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -1191,6 +1191,91 @@ static int nfc_genl_disable_se(struct sk_buff *skb, struct genl_info *info) return rc; } +static int nfc_genl_send_se(struct sk_buff *msg, struct nfc_dev *dev, + u32 portid, u32 seq, + struct netlink_callback *cb, + int flags) +{ + void *hdr; + struct nfc_se *se, *n; + + list_for_each_entry_safe(se, n, &dev->secure_elements, list) { + hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, flags, + NFC_CMD_GET_SE); + if (!hdr) + goto nla_put_failure; + + if (cb) + genl_dump_check_consistent(cb, hdr, &nfc_genl_family); + + if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) || + nla_put_u32(msg, NFC_ATTR_SE_INDEX, se->idx) || + nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type)) + goto nla_put_failure; + + if (genlmsg_end(msg, hdr) < 0) + goto nla_put_failure; + } + + return 0; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int nfc_genl_dump_ses(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0]; + struct nfc_dev *dev = (struct nfc_dev *) cb->args[1]; + bool first_call = false; + + if (!iter) { + first_call = true; + iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL); + if (!iter) + return -ENOMEM; + cb->args[0] = (long) iter; + } + + mutex_lock(&nfc_devlist_mutex); + + cb->seq = nfc_devlist_generation; + + if (first_call) { + nfc_device_iter_init(iter); + dev = nfc_device_iter_next(iter); + } + + while (dev) { + int rc; + + rc = nfc_genl_send_se(skb, dev, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, cb, NLM_F_MULTI); + if (rc < 0) + break; + + dev = nfc_device_iter_next(iter); + } + + mutex_unlock(&nfc_devlist_mutex); + + cb->args[1] = (long) dev; + + return skb->len; +} + +static int nfc_genl_dump_ses_done(struct netlink_callback *cb) +{ + struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0]; + + nfc_device_iter_exit(iter); + kfree(iter); + + return 0; +} + static struct genl_ops nfc_genl_ops[] = { { .cmd = NFC_CMD_GET_DEVICE, @@ -1265,6 +1350,12 @@ static struct genl_ops nfc_genl_ops[] = { .doit = nfc_genl_disable_se, .policy = nfc_genl_policy, }, + { + .cmd = NFC_CMD_GET_SE, + .dumpit = nfc_genl_dump_ses, + .done = nfc_genl_dump_ses_done, + .policy = nfc_genl_policy, + }, }; -- cgit v1.2.3-71-gd317 From ef04158e13e827315680cf8449d9af3bd8dc6280 Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Fri, 19 Jul 2013 14:56:08 +0200 Subject: NFC: Move nfc_fw_download_done() definition from private to public This API must be called by NFC drivers, and its prototype was incorrectly placed. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- include/net/nfc/nfc.h | 2 ++ net/nfc/nfc.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 5f286b726bb6..100595560584 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -224,6 +224,8 @@ int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gt, u8 gt_len); u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len); +int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name); + int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, int ntargets); int nfc_target_lost(struct nfc_dev *dev, u32 target_idx); diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 820a7850c36a..4e2e5a787c4a 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -126,8 +126,6 @@ static inline void nfc_device_iter_exit(struct class_dev_iter *iter) int nfc_fw_download(struct nfc_dev *dev, const char *firmware_name); int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name); -int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name); - int nfc_dev_up(struct nfc_dev *dev); int nfc_dev_down(struct nfc_dev *dev); -- cgit v1.2.3-71-gd317 From 352a5f5fb3ad8f829cfd4248fe6119895bda881f Mon Sep 17 00:00:00 2001 From: Eric Lapuyade Date: Fri, 19 Jul 2013 14:57:55 +0200 Subject: NFC: netlink: Add result of firmware operation to completion event Result is added as an NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS attribute containing the standard errno positive value of the completion result. This event will be sent when the firmare download operation is done and will contain the operation result. Signed-off-by: Eric Lapuyade Signed-off-by: Samuel Ortiz --- include/net/nfc/nfc.h | 3 ++- include/uapi/linux/nfc.h | 2 ++ net/nfc/core.c | 12 ++++++++++-- net/nfc/netlink.c | 4 +++- net/nfc/nfc.h | 3 ++- 5 files changed, 19 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 100595560584..f68ee68e4e3e 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -224,7 +224,8 @@ int nfc_set_remote_general_bytes(struct nfc_dev *dev, u8 *gt, u8 gt_len); u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len); -int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name); +int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name, + u32 result); int nfc_targets_found(struct nfc_dev *dev, struct nfc_target *targets, int ntargets); diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 029921b067fd..29bed72a4ac4 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -146,6 +146,7 @@ enum nfc_commands { * @NFC_ATTR_FIRMWARE_NAME: Free format firmware version * @NFC_ATTR_SE_INDEX: Secure element index * @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED) + * @NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS: Firmware download operation status */ enum nfc_attrs { NFC_ATTR_UNSPEC, @@ -172,6 +173,7 @@ enum nfc_attrs { NFC_ATTR_SE_INDEX, NFC_ATTR_SE_TYPE, NFC_ATTR_SE_AID, + NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS, /* private: internal use only */ __NFC_ATTR_AFTER_LAST }; diff --git a/net/nfc/core.c b/net/nfc/core.c index aad7f8f59784..d252912b8deb 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -77,11 +77,19 @@ error: return rc; } -int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name) +/** + * nfc_fw_download_done - inform that a firmware download was completed + * + * @dev: The nfc device to which firmware was downloaded + * @firmware_name: The firmware filename + * @result: The positive value of a standard errno value + */ +int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name, + u32 result) { dev->fw_download_in_progress = false; - return nfc_genl_fw_download_done(dev, firmware_name); + return nfc_genl_fw_download_done(dev, firmware_name, result); } EXPORT_SYMBOL(nfc_fw_download_done); diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 3b08ef90e045..68063b2025da 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -1114,7 +1114,8 @@ static int nfc_genl_fw_download(struct sk_buff *skb, struct genl_info *info) return rc; } -int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name) +int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name, + u32 result) { struct sk_buff *msg; void *hdr; @@ -1129,6 +1130,7 @@ int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name) goto free_msg; if (nla_put_string(msg, NFC_ATTR_FIRMWARE_NAME, firmware_name) || + nla_put_u32(msg, NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS, result) || nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx)) goto nla_put_failure; diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index 4e2e5a787c4a..aaf606fc1faa 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -124,7 +124,8 @@ static inline void nfc_device_iter_exit(struct class_dev_iter *iter) } int nfc_fw_download(struct nfc_dev *dev, const char *firmware_name); -int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name); +int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name, + u32 result); int nfc_dev_up(struct nfc_dev *dev); -- cgit v1.2.3-71-gd317 From fc4eba58b4c1462ff3d6247b66fb47d6928db6d2 Mon Sep 17 00:00:00 2001 From: Hannes Frederic Sowa Date: Wed, 14 Aug 2013 01:03:46 +0200 Subject: ipv6: make unsolicited report intervals configurable for mld Commit cab70040dfd95ee32144f02fade64f0cb94f31a0 ("net: igmp: Reduce Unsolicited report interval to 1s when using IGMPv3") and 2690048c01f32bf45d1c1e1ab3079bc10ad2aea7 ("net: igmp: Allow user-space configuration of igmp unsolicited report interval") by William Manley made igmp unsolicited report intervals configurable per interface and corrected the interval of unsolicited igmpv3 report messages resendings to 1s. Same needs to be done for IPv6: MLDv1 (RFC2710 7.10.): 10 seconds MLDv2 (RFC3810 9.11.): 1 second Both intervals are configurable via new procfs knobs mldv1_unsolicited_report_interval and mldv2_unsolicited_report_interval. (also added .force_mld_version to ipv6_devconf_dflt to bring structs in line without semantic changes) v2: a) Joined documentation update for IPv4 and IPv6 MLD/IGMP unsolicited_report_interval procfs knobs. b) incorporate stylistic feedback from William Manley v3: a) add new DEVCONF_* values to the end of the enum (thanks to David Miller) Cc: Cong Wang Cc: William Manley Cc: Benjamin LaHaise Cc: YOSHIFUJI Hideaki Signed-off-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 18 ++++++++++++++++++ include/linux/ipv6.h | 2 ++ include/uapi/linux/ipv6.h | 2 ++ net/ipv6/addrconf.c | 25 +++++++++++++++++++++++++ net/ipv6/mcast.c | 17 ++++++++++++++--- 5 files changed, 61 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 36be26b2ef7a..debfe857d8f9 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1039,7 +1039,15 @@ disable_policy - BOOLEAN disable_xfrm - BOOLEAN Disable IPSEC encryption on this interface, whatever the policy +igmpv2_unsolicited_report_interval - INTEGER + The interval in milliseconds in which the next unsolicited + IGMPv1 or IGMPv2 report retransmit will take place. + Default: 10000 (10 seconds) +igmpv3_unsolicited_report_interval - INTEGER + The interval in milliseconds in which the next unsolicited + IGMPv3 report retransmit will take place. + Default: 1000 (1 seconds) tag - INTEGER Allows you to write a number, which can be used as required. @@ -1331,6 +1339,16 @@ ndisc_notify - BOOLEAN 1 - Generate unsolicited neighbour advertisements when device is brought up or hardware address changes. +mldv1_unsolicited_report_interval - INTEGER + The interval in milliseconds in which the next unsolicited + MLDv1 report retransmit will take place. + Default: 10000 (10 seconds) + +mldv2_unsolicited_report_interval - INTEGER + The interval in milliseconds in which the next unsolicited + MLDv2 report retransmit will take place. + Default: 1000 (1 second) + icmp/*: ratelimit - INTEGER Limit the maximal rates for sending ICMPv6 packets. diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 850e95bc766c..77a478462d8e 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -19,6 +19,8 @@ struct ipv6_devconf { __s32 rtr_solicit_interval; __s32 rtr_solicit_delay; __s32 force_mld_version; + __s32 mldv1_unsolicited_report_interval; + __s32 mldv2_unsolicited_report_interval; #ifdef CONFIG_IPV6_PRIVACY __s32 use_tempaddr; __s32 temp_valid_lft; diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 4bda4cf5b0f5..d07ac6903e59 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h @@ -160,6 +160,8 @@ enum { DEVCONF_ACCEPT_DAD, DEVCONF_FORCE_TLLAO, DEVCONF_NDISC_NOTIFY, + DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL, + DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL, DEVCONF_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 7fd8572bac80..ad12f7c43f25 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -177,6 +177,8 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .accept_redirects = 1, .autoconf = 1, .force_mld_version = 0, + .mldv1_unsolicited_report_interval = 10 * HZ, + .mldv2_unsolicited_report_interval = HZ, .dad_transmits = 1, .rtr_solicits = MAX_RTR_SOLICITATIONS, .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, @@ -211,6 +213,9 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .accept_ra = 1, .accept_redirects = 1, .autoconf = 1, + .force_mld_version = 0, + .mldv1_unsolicited_report_interval = 10 * HZ, + .mldv2_unsolicited_report_interval = HZ, .dad_transmits = 1, .rtr_solicits = MAX_RTR_SOLICITATIONS, .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, @@ -4179,6 +4184,10 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_RTR_SOLICIT_DELAY] = jiffies_to_msecs(cnf->rtr_solicit_delay); array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version; + array[DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL] = + jiffies_to_msecs(cnf->mldv1_unsolicited_report_interval); + array[DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL] = + jiffies_to_msecs(cnf->mldv2_unsolicited_report_interval); #ifdef CONFIG_IPV6_PRIVACY array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr; array[DEVCONF_TEMP_VALID_LFT] = cnf->temp_valid_lft; @@ -4862,6 +4871,22 @@ static struct addrconf_sysctl_table .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "mldv1_unsolicited_report_interval", + .data = + &ipv6_devconf.mldv1_unsolicited_report_interval, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_ms_jiffies, + }, + { + .procname = "mldv2_unsolicited_report_interval", + .data = + &ipv6_devconf.mldv2_unsolicited_report_interval, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_ms_jiffies, + }, #ifdef CONFIG_IPV6_PRIVACY { .procname = "use_tempaddr", diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index db25b8eb62bd..6c76df9909bf 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -108,7 +108,6 @@ static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml, struct inet6_dev *idev); -#define IGMP6_UNSOLICITED_IVAL (10*HZ) #define MLD_QRV_DEFAULT 2 #define MLD_V1_SEEN(idev) (dev_net((idev)->dev)->ipv6.devconf_all->force_mld_version == 1 || \ @@ -129,6 +128,18 @@ int sysctl_mld_max_msf __read_mostly = IPV6_MLD_MAX_MSF; pmc != NULL; \ pmc = rcu_dereference(pmc->next)) +static int unsolicited_report_interval(struct inet6_dev *idev) +{ + int iv; + + if (MLD_V1_SEEN(idev)) + iv = idev->cnf.mldv1_unsolicited_report_interval; + else + iv = idev->cnf.mldv2_unsolicited_report_interval; + + return iv > 0 ? iv : 1; +} + int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) { struct net_device *dev = NULL; @@ -2158,7 +2169,7 @@ static void igmp6_join_group(struct ifmcaddr6 *ma) igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT); - delay = net_random() % IGMP6_UNSOLICITED_IVAL; + delay = net_random() % unsolicited_report_interval(ma->idev); spin_lock_bh(&ma->mca_lock); if (del_timer(&ma->mca_timer)) { @@ -2325,7 +2336,7 @@ void ipv6_mc_init_dev(struct inet6_dev *idev) setup_timer(&idev->mc_dad_timer, mld_dad_timer_expire, (unsigned long)idev); idev->mc_qrv = MLD_QRV_DEFAULT; - idev->mc_maxdelay = IGMP6_UNSOLICITED_IVAL; + idev->mc_maxdelay = unsolicited_report_interval(idev); idev->mc_v1_seen = 0; write_unlock_bh(&idev->lock); } -- cgit v1.2.3-71-gd317 From f20104de55a212a9742d8df1807f1f29dc95b748 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 13 Aug 2013 20:22:50 -0400 Subject: cgroup: replace cgroup->css_kill_cnt with ->nr_css Currently, css (cgroup_subsys_state) lifetime is tied to that of the associated cgroup. With the planned unified hierarchy, css's will be dynamically created and destroyed within the lifetime of a cgroup. To enable such usages, css's will be individually RCU protected instead of being tied to the cgroup. cgroup->css_kill_cnt is used during cgroup destruction to wait for css reference count disable; however, this model doesn't work once css's lifetimes are managed separately from cgroup's. This patch replaces it with cgroup->nr_css which is an cgroup_mutex protected integer counting the number of attached css's. The count is incremented from online_css() and decremented after refcnt kill is confirmed. If the count reaches zero and the cgroup is marked dead, the second stage of cgroup destruction is kicked off. If a cgroup doesn't have any css attached at the time of rmdir, cgroup_destroy_locked() now invokes the second stage directly as no css kill confirmation would happen. cgroup_offline_fn() - the second step of cgroup destruction - is renamed to cgroup_destroy_css_killed() and now expects to be called with cgroup_mutex held. While this patch changes how css destruction is punted to work items, it shouldn't change any visible behavior. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 4 +++- kernel/cgroup.c | 52 +++++++++++++++++++++++++++----------------------- 2 files changed, 31 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index eb200b5794e7..80dca872f4d4 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -171,6 +171,9 @@ struct cgroup { */ int id; + /* the number of attached css's */ + int nr_css; + /* * We link our 'sibling' struct into our parent's 'children'. * Our children link their 'sibling' into our 'children'. @@ -234,7 +237,6 @@ struct cgroup { /* For css percpu_ref killing and RCU-protected deletion */ struct rcu_head rcu_head; struct work_struct destroy_work; - atomic_t css_kill_cnt; /* List of events which userspace want to receive */ struct list_head event_list; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 398ffbbee32f..174f4c3d72ef 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -218,7 +218,7 @@ static int need_forkexit_callback __read_mostly; static struct cftype cgroup_base_files[]; -static void cgroup_offline_fn(struct work_struct *work); +static void cgroup_destroy_css_killed(struct cgroup *cgrp); static int cgroup_destroy_locked(struct cgroup *cgrp); static int cgroup_addrm_files(struct cgroup *cgrp, struct cftype cfts[], bool is_add); @@ -4335,6 +4335,7 @@ static int online_css(struct cgroup_subsys_state *css) ret = ss->css_online(css); if (!ret) { css->flags |= CSS_ONLINE; + css->cgroup->nr_css++; rcu_assign_pointer(css->cgroup->subsys[ss->subsys_id], css); } return ret; @@ -4545,16 +4546,6 @@ static int cgroup_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) return cgroup_create(c_parent, dentry, mode | S_IFDIR); } -static void cgroup_css_killed(struct cgroup *cgrp) -{ - if (!atomic_dec_and_test(&cgrp->css_kill_cnt)) - return; - - /* percpu ref's of all css's are killed, kick off the next step */ - INIT_WORK(&cgrp->destroy_work, cgroup_offline_fn); - schedule_work(&cgrp->destroy_work); -} - /* * This is called when the refcnt of a css is confirmed to be killed. * css_tryget() is now guaranteed to fail. @@ -4565,7 +4556,17 @@ static void css_killed_work_fn(struct work_struct *work) container_of(work, struct cgroup_subsys_state, destroy_work); struct cgroup *cgrp = css->cgroup; - cgroup_css_killed(cgrp); + mutex_lock(&cgroup_mutex); + + /* + * If @cgrp is marked dead, it's waiting for refs of all css's to + * be disabled before proceeding to the second phase of cgroup + * destruction. If we are the last one, kick it off. + */ + if (!--cgrp->nr_css && cgroup_is_dead(cgrp)) + cgroup_destroy_css_killed(cgrp); + + mutex_unlock(&cgroup_mutex); } /* css kill confirmation processing requires process context, bounce */ @@ -4634,11 +4635,10 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) * Use percpu_ref_kill_and_confirm() to get notifications as each * css is confirmed to be seen as killed on all CPUs. The * notification callback keeps track of the number of css's to be - * killed and schedules cgroup_offline_fn() to perform the rest of - * destruction once the percpu refs of all css's are confirmed to - * be killed. + * killed and invokes cgroup_destroy_css_killed() to perform the + * rest of destruction once the percpu refs of all css's are + * confirmed to be killed. */ - atomic_set(&cgrp->css_kill_cnt, 1); for_each_root_subsys(cgrp->root, ss) { struct cgroup_subsys_state *css = cgroup_css(cgrp, ss->subsys_id); @@ -4648,10 +4648,8 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) */ percpu_ref_get(&css->refcnt); - atomic_inc(&cgrp->css_kill_cnt); percpu_ref_kill_and_confirm(&css->refcnt, css_killed_ref_fn); } - cgroup_css_killed(cgrp); /* * Mark @cgrp dead. This prevents further task migration and child @@ -4668,6 +4666,15 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) list_del_init(&cgrp->release_list); raw_spin_unlock(&release_list_lock); + /* + * If @cgrp has css's attached, the second stage of cgroup + * destruction is kicked off from css_killed_work_fn() after the + * refs of all attached css's are killed. If @cgrp doesn't have + * any css, we kick it off here. + */ + if (!cgrp->nr_css) + cgroup_destroy_css_killed(cgrp); + /* * Clear and remove @cgrp directory. The removal puts the base ref * but we aren't quite done with @cgrp yet, so hold onto it. @@ -4693,7 +4700,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) }; /** - * cgroup_offline_fn - the second step of cgroup destruction + * cgroup_destroy_css_killed - the second step of cgroup destruction * @work: cgroup->destroy_free_work * * This function is invoked from a work item for a cgroup which is being @@ -4702,14 +4709,13 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) * is the second step of destruction described in the comment above * cgroup_destroy_locked(). */ -static void cgroup_offline_fn(struct work_struct *work) +static void cgroup_destroy_css_killed(struct cgroup *cgrp) { - struct cgroup *cgrp = container_of(work, struct cgroup, destroy_work); struct cgroup *parent = cgrp->parent; struct dentry *d = cgrp->dentry; struct cgroup_subsys *ss; - mutex_lock(&cgroup_mutex); + lockdep_assert_held(&cgroup_mutex); /* * css_tryget() is guaranteed to fail now. Tell subsystems to @@ -4743,8 +4749,6 @@ static void cgroup_offline_fn(struct work_struct *work) set_bit(CGRP_RELEASABLE, &parent->flags); check_for_release(parent); - - mutex_unlock(&cgroup_mutex); } static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry) -- cgit v1.2.3-71-gd317 From 09a503ea3a816b285b0b402b7f785eaec0c7a7e1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 13 Aug 2013 20:22:50 -0400 Subject: cgroup: decouple cgroup_subsys_state destruction from cgroup destruction Currently, css (cgroup_subsys_state) lifetime is tied to that of the associated cgroup. css's are created when the associated cgroup is created and destroyed when it gets destroyed. Also, individual css's aren't RCU protected but the whole cgroup is. With the planned unified hierarchy, css's will need to be dynamically created and destroyed within the lifetime of a cgroup. To enable such usages, this patch decouples css destruction from cgroup destruction - offline_css() invocation and the final css_put() are moved from cgroup_destroy_css_killed() to css_killed_work_fn(). Now each css is individually offlined and put as its reference count is killed instead of waiting for all css's attached to the cgroup to finish refcnt killing and then proceeding to offlining and putting them together. While this changes the order of destruction operations, the changes shouldn't be noticeable to cgroup subsystems or userland. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 2 +- kernel/cgroup.c | 52 +++++++++++++++++++++++--------------------------- 2 files changed, 25 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 80dca872f4d4..71e77e7cdb6f 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -82,7 +82,7 @@ struct cgroup_subsys_state { /* ID for this css, if possible */ struct css_id __rcu *id; - /* Used to put @cgroup->dentry on the last css_put() */ + /* percpu_ref killing and putting dentry on the last css_put() */ struct work_struct destroy_work; }; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 174f4c3d72ef..3c4c4b01ffe5 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4355,6 +4355,7 @@ static void offline_css(struct cgroup_subsys_state *css) ss->css_offline(css); css->flags &= ~CSS_ONLINE; + css->cgroup->nr_css--; } /* @@ -4558,15 +4559,30 @@ static void css_killed_work_fn(struct work_struct *work) mutex_lock(&cgroup_mutex); + /* + * css_tryget() is guaranteed to fail now. Tell subsystems to + * initate destruction. + */ + offline_css(css); + /* * If @cgrp is marked dead, it's waiting for refs of all css's to * be disabled before proceeding to the second phase of cgroup * destruction. If we are the last one, kick it off. */ - if (!--cgrp->nr_css && cgroup_is_dead(cgrp)) + if (!cgrp->nr_css && cgroup_is_dead(cgrp)) cgroup_destroy_css_killed(cgrp); mutex_unlock(&cgroup_mutex); + + /* + * Put the css refs from kill_css(). Each css holds an extra + * reference to the cgroup's dentry and cgroup removal proceeds + * regardless of css refs. On the last put of each css, whenever + * that may be, the extra dentry ref is put so that dentry + * destruction happens only after all css's are released. + */ + css_put(css); } /* css kill confirmation processing requires process context, bounce */ @@ -4633,11 +4649,10 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) * as killed on all CPUs on return. * * Use percpu_ref_kill_and_confirm() to get notifications as each - * css is confirmed to be seen as killed on all CPUs. The - * notification callback keeps track of the number of css's to be - * killed and invokes cgroup_destroy_css_killed() to perform the - * rest of destruction once the percpu refs of all css's are - * confirmed to be killed. + * css is confirmed to be seen as killed on all CPUs. + * cgroup_destroy_css_killed() will be invoked to perform the rest + * of destruction once the percpu refs of all css's are confirmed + * to be killed. */ for_each_root_subsys(cgrp->root, ss) { struct cgroup_subsys_state *css = cgroup_css(cgrp, ss->subsys_id); @@ -4704,36 +4719,17 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) * @work: cgroup->destroy_free_work * * This function is invoked from a work item for a cgroup which is being - * destroyed after the percpu refcnts of all css's are guaranteed to be - * seen as killed on all CPUs, and performs the rest of destruction. This - * is the second step of destruction described in the comment above - * cgroup_destroy_locked(). + * destroyed after all css's are offlined and performs the rest of + * destruction. This is the second step of destruction described in the + * comment above cgroup_destroy_locked(). */ static void cgroup_destroy_css_killed(struct cgroup *cgrp) { struct cgroup *parent = cgrp->parent; struct dentry *d = cgrp->dentry; - struct cgroup_subsys *ss; lockdep_assert_held(&cgroup_mutex); - /* - * css_tryget() is guaranteed to fail now. Tell subsystems to - * initate destruction. - */ - for_each_root_subsys(cgrp->root, ss) - offline_css(cgroup_css(cgrp, ss->subsys_id)); - - /* - * Put the css refs from cgroup_destroy_locked(). Each css holds - * an extra reference to the cgroup's dentry and cgroup removal - * proceeds regardless of css refs. On the last put of each css, - * whenever that may be, the extra dentry ref is put so that dentry - * destruction happens only after all css's are released. - */ - for_each_root_subsys(cgrp->root, ss) - css_put(cgroup_css(cgrp, ss->subsys_id)); - /* delete this cgroup from parent->children */ list_del_rcu(&cgrp->sibling); -- cgit v1.2.3-71-gd317 From 0c21ead136a900c36f1ab74fd7d09a306dc31324 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 13 Aug 2013 20:22:51 -0400 Subject: cgroup: RCU protect each cgroup_subsys_state release With the planned unified hierarchy, individual css's will be created and destroyed dynamically across the lifetime of a cgroup. To enable such usages, css destruction is being decoupled from cgroup destruction. Most of the destruction path has been decoupled but the actual free of css still depends on cgroup free path. When all css refs are drained, css_release() kicks off css_free_work_fn() which puts the cgroup. When the cgroup refcnt reaches zero, cgroup_diput() is invoked which in turn schedules RCU free of the cgroup. After a grace period, all css's are freed along with the cgroup itself. This patch moves the RCU grace period and css freeing from cgroup release path to css release path. css_release(), instead of kicking off css_free_work_fn() directly, schedules RCU callback css_free_rcu_fn() which in turn kicks off css_free_work_fn() after a RCU grace period. css_free_work_fn() is updated to free the css directly. The five-way punting - percpu ref kill confirmation, a work item, percpu ref release, RCU grace period, and again a work item - is quite hairy but the work items are there only to provide process context and the actual sequence is kill confirm -> release -> RCU free, which isn't simple but not too crazy. This removes cgroup_css() usage after offline_css() allowing clearing cgroup->subsys[] from offline_css(), which makes it consistent with online_css() and brings it closer to proper lifetime management for individual css's. Signed-off-by: Tejun Heo Acked-by: Li Zefan --- include/linux/cgroup.h | 3 ++- kernel/cgroup.c | 53 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 39 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 71e77e7cdb6f..c24bd0b9f93a 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -82,7 +82,8 @@ struct cgroup_subsys_state { /* ID for this css, if possible */ struct css_id __rcu *id; - /* percpu_ref killing and putting dentry on the last css_put() */ + /* percpu_ref killing and RCU release */ + struct rcu_head rcu_head; struct work_struct destroy_work; }; diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 3137e38995b0..66d01078eebe 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -869,18 +869,8 @@ static struct cgroup_name *cgroup_alloc_name(struct dentry *dentry) static void cgroup_free_fn(struct work_struct *work) { struct cgroup *cgrp = container_of(work, struct cgroup, destroy_work); - struct cgroup_subsys *ss; mutex_lock(&cgroup_mutex); - /* - * Release the subsystem state objects. - */ - for_each_root_subsys(cgrp->root, ss) { - struct cgroup_subsys_state *css = cgroup_css(cgrp, ss->subsys_id); - - ss->css_free(css); - } - cgrp->root->number_of_cgroups--; mutex_unlock(&cgroup_mutex); @@ -4281,32 +4271,62 @@ err: return ret; } +/* + * css destruction is four-stage process. + * + * 1. Destruction starts. Killing of the percpu_ref is initiated. + * Implemented in kill_css(). + * + * 2. When the percpu_ref is confirmed to be visible as killed on all CPUs + * and thus css_tryget() is guaranteed to fail, the css can be offlined + * by invoking offline_css(). After offlining, the base ref is put. + * Implemented in css_killed_work_fn(). + * + * 3. When the percpu_ref reaches zero, the only possible remaining + * accessors are inside RCU read sections. css_release() schedules the + * RCU callback. + * + * 4. After the grace period, the css can be freed. Implemented in + * css_free_work_fn(). + * + * It is actually hairier because both step 2 and 4 require process context + * and thus involve punting to css->destroy_work adding two additional + * steps to the already complex sequence. + */ static void css_free_work_fn(struct work_struct *work) { struct cgroup_subsys_state *css = container_of(work, struct cgroup_subsys_state, destroy_work); + struct cgroup *cgrp = css->cgroup; if (css->parent) css_put(css->parent); - cgroup_dput(css->cgroup); + css->ss->css_free(css); + cgroup_dput(cgrp); } -static void css_release(struct percpu_ref *ref) +static void css_free_rcu_fn(struct rcu_head *rcu_head) { struct cgroup_subsys_state *css = - container_of(ref, struct cgroup_subsys_state, refcnt); + container_of(rcu_head, struct cgroup_subsys_state, rcu_head); /* * css holds an extra ref to @cgrp->dentry which is put on the last - * css_put(). dput() requires process context, which css_put() may - * be called without. @css->destroy_work will be used to invoke - * dput() asynchronously from css_put(). + * css_put(). dput() requires process context which we don't have. */ INIT_WORK(&css->destroy_work, css_free_work_fn); schedule_work(&css->destroy_work); } +static void css_release(struct percpu_ref *ref) +{ + struct cgroup_subsys_state *css = + container_of(ref, struct cgroup_subsys_state, refcnt); + + call_rcu(&css->rcu_head, css_free_rcu_fn); +} + static void init_css(struct cgroup_subsys_state *css, struct cgroup_subsys *ss, struct cgroup *cgrp) { @@ -4356,6 +4376,7 @@ static void offline_css(struct cgroup_subsys_state *css) css->flags &= ~CSS_ONLINE; css->cgroup->nr_css--; + RCU_INIT_POINTER(css->cgroup->subsys[ss->subsys_id], css); } /* -- cgit v1.2.3-71-gd317 From 36009d07b79d2a168d6037947357d96e5d8cebe7 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 6 Aug 2013 18:08:41 +0200 Subject: tracing/perf: Expand TRACE_EVENT(sched_stat_runtime) To simplify the review of the next patches: 1. We are going to reimplent __perf_task/counter and embedd them into TP_ARGS(). expand TRACE_EVENT(sched_stat_runtime) into DECLARE_EVENT_CLASS() + DEFINE_EVENT(), this way they can use different TP_ARGS's. 2. Change perf_trace_##call() macro to do perf_fetch_caller_regs() right before perf_trace_buf_prepare(). This way it evaluates TP_ARGS() asap, the next patch explores this fact. Note: after 87f44bbc perf_trace_buf_prepare() doesn't need "struct pt_regs *regs", perhaps it makes sense to remove this argument. And perhaps we can teach perf_trace_buf_submit() to accept regs == NULL and do fetch_caller_regs(CALLER_ADDR1) in this case. 3. Cosmetic, but the typecast from "void*" buys nothing. It just adds the noise, remove it. Link: http://lkml.kernel.org/r/20130806160841.GA2736@redhat.com Acked-by: Peter Zijlstra Tested-by: David Ahern Signed-off-by: Oleg Nesterov Signed-off-by: Steven Rostedt --- include/trace/events/sched.h | 6 +++++- include/trace/ftrace.h | 7 +++---- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index e5586caff67a..249c024e67ae 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -372,7 +372,7 @@ DEFINE_EVENT(sched_stat_template, sched_stat_blocked, * Tracepoint for accounting runtime (time the task is executing * on a CPU). */ -TRACE_EVENT(sched_stat_runtime, +DECLARE_EVENT_CLASS(sched_stat_runtime, TP_PROTO(struct task_struct *tsk, u64 runtime, u64 vruntime), @@ -401,6 +401,10 @@ TRACE_EVENT(sched_stat_runtime, (unsigned long long)__entry->vruntime) ); +DEFINE_EVENT(sched_stat_runtime, sched_stat_runtime, + TP_PROTO(struct task_struct *tsk, u64 runtime, u64 vruntime), + TP_ARGS(tsk, runtime, vruntime)); + /* * Tracepoint for showing priority inheritance modifying a tasks * priority. diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 41a6643e2136..618af05f0be6 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -663,15 +663,14 @@ perf_trace_##call(void *__data, proto) \ int __data_size; \ int rctx; \ \ - perf_fetch_caller_regs(&__regs); \ - \ __data_size = ftrace_get_offsets_##call(&__data_offsets, args); \ __entry_size = ALIGN(__data_size + sizeof(*entry) + sizeof(u32),\ sizeof(u64)); \ __entry_size -= sizeof(u32); \ \ - entry = (struct ftrace_raw_##call *)perf_trace_buf_prepare( \ - __entry_size, event_call->event.type, &__regs, &rctx); \ + perf_fetch_caller_regs(&__regs); \ + entry = perf_trace_buf_prepare(__entry_size, \ + event_call->event.type, &__regs, &rctx); \ if (!entry) \ return; \ \ -- cgit v1.2.3-71-gd317 From 12473965c38a527a0c6f7a38d23edce60957f873 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 6 Aug 2013 18:08:44 +0200 Subject: tracing/perf: Reimplement TP_perf_assign() logic The next patch tries to avoid the costly perf_trace_buf_* calls when possible but there is a problem. We can only do this if __task == NULL, perf_tp_event(task != NULL) has the additional code for this case. Unfortunately, TP_perf_assign/__perf_xxx which changes the default values of __count/__task variables for perf_trace_buf_submit() is called "too late", after we already did perf_trace_buf_prepare(), and the optimization above can't work. So this patch simply embeds __perf_xxx() into TP_ARGS(), this way DECLARE_EVENT_CLASS() can use the result of assignments hidden in "args" right after ftrace_get_offsets_##call() which is mostly trivial. This allows us to have the fast-path "__task != NULL" check at the start, see the next patch. Link: http://lkml.kernel.org/r/20130806160844.GA2739@redhat.com Tested-by: David Ahern Acked-by: Peter Zijlstra Signed-off-by: Oleg Nesterov Signed-off-by: Steven Rostedt --- include/trace/events/sched.h | 16 +++------------- include/trace/ftrace.h | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 249c024e67ae..2e7d9947a10d 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -57,7 +57,7 @@ DECLARE_EVENT_CLASS(sched_wakeup_template, TP_PROTO(struct task_struct *p, int success), - TP_ARGS(p, success), + TP_ARGS(__perf_task(p), success), TP_STRUCT__entry( __array( char, comm, TASK_COMM_LEN ) @@ -73,9 +73,6 @@ DECLARE_EVENT_CLASS(sched_wakeup_template, __entry->prio = p->prio; __entry->success = success; __entry->target_cpu = task_cpu(p); - ) - TP_perf_assign( - __perf_task(p); ), TP_printk("comm=%s pid=%d prio=%d success=%d target_cpu=%03d", @@ -313,7 +310,7 @@ DECLARE_EVENT_CLASS(sched_stat_template, TP_PROTO(struct task_struct *tsk, u64 delay), - TP_ARGS(tsk, delay), + TP_ARGS(__perf_task(tsk), __perf_count(delay)), TP_STRUCT__entry( __array( char, comm, TASK_COMM_LEN ) @@ -325,10 +322,6 @@ DECLARE_EVENT_CLASS(sched_stat_template, memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN); __entry->pid = tsk->pid; __entry->delay = delay; - ) - TP_perf_assign( - __perf_count(delay); - __perf_task(tsk); ), TP_printk("comm=%s pid=%d delay=%Lu [ns]", @@ -376,7 +369,7 @@ DECLARE_EVENT_CLASS(sched_stat_runtime, TP_PROTO(struct task_struct *tsk, u64 runtime, u64 vruntime), - TP_ARGS(tsk, runtime, vruntime), + TP_ARGS(tsk, __perf_count(runtime), vruntime), TP_STRUCT__entry( __array( char, comm, TASK_COMM_LEN ) @@ -390,9 +383,6 @@ DECLARE_EVENT_CLASS(sched_stat_runtime, __entry->pid = tsk->pid; __entry->runtime = runtime; __entry->vruntime = vruntime; - ) - TP_perf_assign( - __perf_count(runtime); ), TP_printk("comm=%s pid=%d runtime=%Lu [ns] vruntime=%Lu [ns]", diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 618af05f0be6..4163d93ccf38 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -507,8 +507,14 @@ static inline notrace int ftrace_get_offsets_##call( \ #undef TP_fast_assign #define TP_fast_assign(args...) args -#undef TP_perf_assign -#define TP_perf_assign(args...) +#undef __perf_addr +#define __perf_addr(a) (a) + +#undef __perf_count +#define __perf_count(c) (c) + +#undef __perf_task +#define __perf_task(t) (t) #undef DECLARE_EVENT_CLASS #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ @@ -636,16 +642,13 @@ __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call #define __get_str(field) (char *)__get_dynamic_array(field) #undef __perf_addr -#define __perf_addr(a) __addr = (a) +#define __perf_addr(a) (__addr = (a)) #undef __perf_count -#define __perf_count(c) __count = (c) +#define __perf_count(c) (__count = (c)) #undef __perf_task -#define __perf_task(t) __task = (t) - -#undef TP_perf_assign -#define TP_perf_assign(args...) args +#define __perf_task(t) (__task = (t)) #undef DECLARE_EVENT_CLASS #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ -- cgit v1.2.3-71-gd317 From d027e6a9c83440bf1ca9e5503539d58d8e0914f1 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 6 Aug 2013 18:08:47 +0200 Subject: tracing/perf: Avoid perf_trace_buf_*() in perf_trace_##call() when possible perf_trace_buf_prepare() + perf_trace_buf_submit(task => NULL) make no sense if hlist_empty(head). Change perf_trace_##call() to check ->perf_events beforehand and do nothing if it is empty. This removes the overhead for tasks without events associated with them. For example, "perf record -e sched:sched_switch -p1" attaches the counter(s) to the single task, but every task in system will do perf_trace_buf_prepare/submit() just to realize that it was not attached to this event. However, we can only do this if __task == NULL, so we also add the __builtin_constant_p(__task) check. With this patch "perf bench sched pipe" shows approximately 4% improvement when "perf record -p1" runs in parallel, many thanks to Steven for the testing. Link: http://lkml.kernel.org/r/20130806160847.GA2746@redhat.com Tested-by: David Ahern Acked-by: Peter Zijlstra Signed-off-by: Oleg Nesterov Signed-off-by: Steven Rostedt --- include/trace/ftrace.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 4163d93ccf38..5c7ab17cbb02 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -667,6 +667,12 @@ perf_trace_##call(void *__data, proto) \ int rctx; \ \ __data_size = ftrace_get_offsets_##call(&__data_offsets, args); \ + \ + head = this_cpu_ptr(event_call->perf_events); \ + if (__builtin_constant_p(!__task) && !__task && \ + hlist_empty(head)) \ + return; \ + \ __entry_size = ALIGN(__data_size + sizeof(*entry) + sizeof(u32),\ sizeof(u64)); \ __entry_size -= sizeof(u32); \ @@ -681,7 +687,6 @@ perf_trace_##call(void *__data, proto) \ \ { assign; } \ \ - head = this_cpu_ptr(event_call->perf_events); \ perf_trace_buf_submit(entry, __entry_size, rctx, __addr, \ __count, &__regs, head, __task); \ } -- cgit v1.2.3-71-gd317 From 13b3006b8ebd60926a60fc378ff6fe8affa9a194 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 10 Aug 2013 18:52:18 +0200 Subject: dma: mmp_pdma: add filter function PXA peripherals need to obtain specific DMA request ids which will eventually be stored in the DRCMR register. Currently, clients are expected to store that number inside the slave config block as slave_id, which is unfortunately incompatible with the way DMA resources are handled in DT environments. This patch adds a filter function which stores the filter parameter passed in by of-dma.c into the channel's drcmr register. For backward compatability, cfg->slave_id is still used if set to a non-zero value. Signed-off-by: Daniel Mack Acked-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/mmp_pdma.c | 21 ++++++++++++++++++++- include/linux/dma/mmp-pdma.h | 15 +++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 include/linux/dma/mmp-pdma.h (limited to 'include') diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index eb75d4b4bafe..dd024d4759e7 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "dmaengine.h" @@ -635,8 +636,13 @@ static int mmp_pdma_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd, chan->dcmd |= DCMD_BURST32; chan->dir = cfg->direction; - chan->drcmr = cfg->slave_id; chan->dev_addr = addr; + /* FIXME: drivers should be ported over to use the filter + * function. Once that's done, the following two lines can + * be removed. + */ + if (cfg->slave_id) + chan->drcmr = cfg->slave_id; break; default: return -ENOSYS; @@ -877,6 +883,19 @@ static struct platform_driver mmp_pdma_driver = { .remove = mmp_pdma_remove, }; +bool mmp_pdma_filter_fn(struct dma_chan *chan, void *param) +{ + struct mmp_pdma_chan *c = to_mmp_pdma_chan(chan); + + if (chan->device->dev->driver != &mmp_pdma_driver.driver) + return false; + + c->drcmr = *(unsigned int *) param; + + return true; +} +EXPORT_SYMBOL_GPL(mmp_pdma_filter_fn); + module_platform_driver(mmp_pdma_driver); MODULE_DESCRIPTION("MARVELL MMP Periphera DMA Driver"); diff --git a/include/linux/dma/mmp-pdma.h b/include/linux/dma/mmp-pdma.h new file mode 100644 index 000000000000..2dc9b2bc18fc --- /dev/null +++ b/include/linux/dma/mmp-pdma.h @@ -0,0 +1,15 @@ +#ifndef _MMP_PDMA_H_ +#define _MMP_PDMA_H_ + +struct dma_chan; + +#ifdef CONFIG_MMP_PDMA +bool mmp_pdma_filter_fn(struct dma_chan *chan, void *param); +#else +static inline bool mmp_pdma_filter_fn(struct dma_chan *chan, void *param) +{ + return false; +} +#endif + +#endif /* _MMP_PDMA_H_ */ -- cgit v1.2.3-71-gd317 From f0c03956ac40fdc4fbce7bed262ae74ac372e765 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Tue, 13 Aug 2013 15:05:38 +0200 Subject: netfilter: export xt_rpfilter.h to userland This file contains the API for the match "rpfilter", hence it should be exported to userland. Signed-off-by: Nicolas Dichtel Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/xt_rpfilter.h | 23 ----------------------- include/uapi/linux/netfilter/Kbuild | 1 + include/uapi/linux/netfilter/xt_rpfilter.h | 23 +++++++++++++++++++++++ 3 files changed, 24 insertions(+), 23 deletions(-) delete mode 100644 include/linux/netfilter/xt_rpfilter.h create mode 100644 include/uapi/linux/netfilter/xt_rpfilter.h (limited to 'include') diff --git a/include/linux/netfilter/xt_rpfilter.h b/include/linux/netfilter/xt_rpfilter.h deleted file mode 100644 index 8358d4f71952..000000000000 --- a/include/linux/netfilter/xt_rpfilter.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _XT_RPATH_H -#define _XT_RPATH_H - -#include - -enum { - XT_RPFILTER_LOOSE = 1 << 0, - XT_RPFILTER_VALID_MARK = 1 << 1, - XT_RPFILTER_ACCEPT_LOCAL = 1 << 2, - XT_RPFILTER_INVERT = 1 << 3, -#ifdef __KERNEL__ - XT_RPFILTER_OPTION_MASK = XT_RPFILTER_LOOSE | - XT_RPFILTER_VALID_MARK | - XT_RPFILTER_ACCEPT_LOCAL | - XT_RPFILTER_INVERT, -#endif -}; - -struct xt_rpfilter_info { - __u8 flags; -}; - -#endif diff --git a/include/uapi/linux/netfilter/Kbuild b/include/uapi/linux/netfilter/Kbuild index 41115776d76f..dc00927ffd62 100644 --- a/include/uapi/linux/netfilter/Kbuild +++ b/include/uapi/linux/netfilter/Kbuild @@ -68,6 +68,7 @@ header-y += xt_quota.h header-y += xt_rateest.h header-y += xt_realm.h header-y += xt_recent.h +header-y += xt_rpfilter.h header-y += xt_sctp.h header-y += xt_set.h header-y += xt_socket.h diff --git a/include/uapi/linux/netfilter/xt_rpfilter.h b/include/uapi/linux/netfilter/xt_rpfilter.h new file mode 100644 index 000000000000..8358d4f71952 --- /dev/null +++ b/include/uapi/linux/netfilter/xt_rpfilter.h @@ -0,0 +1,23 @@ +#ifndef _XT_RPATH_H +#define _XT_RPATH_H + +#include + +enum { + XT_RPFILTER_LOOSE = 1 << 0, + XT_RPFILTER_VALID_MARK = 1 << 1, + XT_RPFILTER_ACCEPT_LOCAL = 1 << 2, + XT_RPFILTER_INVERT = 1 << 3, +#ifdef __KERNEL__ + XT_RPFILTER_OPTION_MASK = XT_RPFILTER_LOOSE | + XT_RPFILTER_VALID_MARK | + XT_RPFILTER_ACCEPT_LOCAL | + XT_RPFILTER_INVERT, +#endif +}; + +struct xt_rpfilter_info { + __u8 flags; +}; + +#endif -- cgit v1.2.3-71-gd317 From 38c67328ac79cb9eaf61b5d4750fe3b9cff0dd15 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Tue, 13 Aug 2013 15:05:39 +0200 Subject: netfilter: export xt_HMARK.h to userland This file contains the API for the target "HMARK", hence it should be exported to userland. Signed-off-by: Nicolas Dichtel Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/xt_HMARK.h | 50 --------------------------------- include/uapi/linux/netfilter/Kbuild | 1 + include/uapi/linux/netfilter/xt_HMARK.h | 50 +++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 50 deletions(-) delete mode 100644 include/linux/netfilter/xt_HMARK.h create mode 100644 include/uapi/linux/netfilter/xt_HMARK.h (limited to 'include') diff --git a/include/linux/netfilter/xt_HMARK.h b/include/linux/netfilter/xt_HMARK.h deleted file mode 100644 index 826fc5807577..000000000000 --- a/include/linux/netfilter/xt_HMARK.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef XT_HMARK_H_ -#define XT_HMARK_H_ - -#include - -enum { - XT_HMARK_SADDR_MASK, - XT_HMARK_DADDR_MASK, - XT_HMARK_SPI, - XT_HMARK_SPI_MASK, - XT_HMARK_SPORT, - XT_HMARK_DPORT, - XT_HMARK_SPORT_MASK, - XT_HMARK_DPORT_MASK, - XT_HMARK_PROTO_MASK, - XT_HMARK_RND, - XT_HMARK_MODULUS, - XT_HMARK_OFFSET, - XT_HMARK_CT, - XT_HMARK_METHOD_L3, - XT_HMARK_METHOD_L3_4, -}; -#define XT_HMARK_FLAG(flag) (1 << flag) - -union hmark_ports { - struct { - __u16 src; - __u16 dst; - } p16; - struct { - __be16 src; - __be16 dst; - } b16; - __u32 v32; - __be32 b32; -}; - -struct xt_hmark_info { - union nf_inet_addr src_mask; - union nf_inet_addr dst_mask; - union hmark_ports port_mask; - union hmark_ports port_set; - __u32 flags; - __u16 proto_mask; - __u32 hashrnd; - __u32 hmodulus; - __u32 hoffset; /* Mark offset to start from */ -}; - -#endif /* XT_HMARK_H_ */ diff --git a/include/uapi/linux/netfilter/Kbuild b/include/uapi/linux/netfilter/Kbuild index dc00927ffd62..174915420d3f 100644 --- a/include/uapi/linux/netfilter/Kbuild +++ b/include/uapi/linux/netfilter/Kbuild @@ -22,6 +22,7 @@ header-y += xt_CONNMARK.h header-y += xt_CONNSECMARK.h header-y += xt_CT.h header-y += xt_DSCP.h +header-y += xt_HMARK.h header-y += xt_IDLETIMER.h header-y += xt_LED.h header-y += xt_LOG.h diff --git a/include/uapi/linux/netfilter/xt_HMARK.h b/include/uapi/linux/netfilter/xt_HMARK.h new file mode 100644 index 000000000000..826fc5807577 --- /dev/null +++ b/include/uapi/linux/netfilter/xt_HMARK.h @@ -0,0 +1,50 @@ +#ifndef XT_HMARK_H_ +#define XT_HMARK_H_ + +#include + +enum { + XT_HMARK_SADDR_MASK, + XT_HMARK_DADDR_MASK, + XT_HMARK_SPI, + XT_HMARK_SPI_MASK, + XT_HMARK_SPORT, + XT_HMARK_DPORT, + XT_HMARK_SPORT_MASK, + XT_HMARK_DPORT_MASK, + XT_HMARK_PROTO_MASK, + XT_HMARK_RND, + XT_HMARK_MODULUS, + XT_HMARK_OFFSET, + XT_HMARK_CT, + XT_HMARK_METHOD_L3, + XT_HMARK_METHOD_L3_4, +}; +#define XT_HMARK_FLAG(flag) (1 << flag) + +union hmark_ports { + struct { + __u16 src; + __u16 dst; + } p16; + struct { + __be16 src; + __be16 dst; + } b16; + __u32 v32; + __be32 b32; +}; + +struct xt_hmark_info { + union nf_inet_addr src_mask; + union nf_inet_addr dst_mask; + union hmark_ports port_mask; + union hmark_ports port_set; + __u32 flags; + __u16 proto_mask; + __u32 hashrnd; + __u32 hmodulus; + __u32 hoffset; /* Mark offset to start from */ +}; + +#endif /* XT_HMARK_H_ */ -- cgit v1.2.3-71-gd317 From 7cabf491eb6d52ea7f166b287702890cae0c803d Mon Sep 17 00:00:00 2001 From: Varun Sethi Date: Mon, 15 Jul 2013 10:20:56 +0530 Subject: iommu/fsl: Add additional iommu attributes required by the PAMU driver. Added the following domain attributes for the FSL PAMU driver: 1. Added new iommu stash attribute, which allows setting of the LIODN specific stash id parameter through IOMMU API. 2. Added an attribute for enabling/disabling DMA to a particular memory window. 3. Added domain attribute to check for PAMUV1 specific constraints. Signed-off-by: Varun Sethi Signed-off-by: Joerg Roedel --- arch/powerpc/include/asm/fsl_pamu_stash.h | 39 +++++++++++++++++++++++++++++++ include/linux/iommu.h | 16 +++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 arch/powerpc/include/asm/fsl_pamu_stash.h (limited to 'include') diff --git a/arch/powerpc/include/asm/fsl_pamu_stash.h b/arch/powerpc/include/asm/fsl_pamu_stash.h new file mode 100644 index 000000000000..caa1b21c25cd --- /dev/null +++ b/arch/powerpc/include/asm/fsl_pamu_stash.h @@ -0,0 +1,39 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + */ + +#ifndef __FSL_PAMU_STASH_H +#define __FSL_PAMU_STASH_H + +/* cache stash targets */ +enum pamu_stash_target { + PAMU_ATTR_CACHE_L1 = 1, + PAMU_ATTR_CACHE_L2, + PAMU_ATTR_CACHE_L3, +}; + +/* + * This attribute allows configuring stashig specific parameters + * in the PAMU hardware. + */ + +struct pamu_stash_attribute { + u32 cpu; /* cpu number */ + u32 cache; /* cache to stash to: L1,L2,L3 */ +}; + +#endif /* __FSL_PAMU_STASH_H */ diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 3aeb7305e2f5..7ea319e95b47 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -58,10 +58,26 @@ struct iommu_domain { #define IOMMU_CAP_CACHE_COHERENCY 0x1 #define IOMMU_CAP_INTR_REMAP 0x2 /* isolates device intrs */ +/* + * Following constraints are specifc to FSL_PAMUV1: + * -aperture must be power of 2, and naturally aligned + * -number of windows must be power of 2, and address space size + * of each window is determined by aperture size / # of windows + * -the actual size of the mapped region of a window must be power + * of 2 starting with 4KB and physical address must be naturally + * aligned. + * DOMAIN_ATTR_FSL_PAMUV1 corresponds to the above mentioned contraints. + * The caller can invoke iommu_domain_get_attr to check if the underlying + * iommu implementation supports these constraints. + */ + enum iommu_attr { DOMAIN_ATTR_GEOMETRY, DOMAIN_ATTR_PAGING, DOMAIN_ATTR_WINDOWS, + DOMAIN_ATTR_FSL_PAMU_STASH, + DOMAIN_ATTR_FSL_PAMU_ENABLE, + DOMAIN_ATTR_FSL_PAMUV1, DOMAIN_ATTR_MAX, }; -- cgit v1.2.3-71-gd317 From 65f382fd0c8fa483713c0971de9f1dfb4cf1ad9c Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 11 Jul 2013 19:12:32 +0200 Subject: context_tracking: Ground setup for static key use Prepare for using a static key in the context tracking subsystem. This will help optimizing the off case on its many users: * user_enter, user_exit, exception_enter, exception_exit, guest_enter, guest_exit, vtime_*() Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman --- include/linux/context_tracking.h | 10 ++++++++++ init/main.c | 2 ++ kernel/context_tracking.c | 23 +++++++++++++++++------ 3 files changed, 29 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index 1ae37c708c63..c138c24bad1a 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -4,6 +4,7 @@ #include #include #include +#include #include struct context_tracking { @@ -22,6 +23,7 @@ struct context_tracking { #ifdef CONFIG_CONTEXT_TRACKING +extern struct static_key context_tracking_enabled; DECLARE_PER_CPU(struct context_tracking, context_tracking); static inline bool context_tracking_in_user(void) @@ -67,6 +69,14 @@ static inline void context_tracking_task_switch(struct task_struct *prev, struct task_struct *next) { } #endif /* !CONFIG_CONTEXT_TRACKING */ + +#ifdef CONFIG_CONTEXT_TRACKING_FORCE +extern void context_tracking_init(void); +#else +static inline void context_tracking_init(void) { } +#endif /* CONFIG_CONTEXT_TRACKING_FORCE */ + + #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN extern void guest_enter(void); extern void guest_exit(void); diff --git a/init/main.c b/init/main.c index d03d2ec2eacf..af310afbef28 100644 --- a/init/main.c +++ b/init/main.c @@ -75,6 +75,7 @@ #include #include #include +#include #include #include @@ -545,6 +546,7 @@ asmlinkage void __init start_kernel(void) idr_init_cache(); rcu_init(); tick_nohz_init(); + context_tracking_init(); radix_tree_init(); /* init some links before init_ISA_irqs() */ early_irq_init(); diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index 72bcb2570d3e..839d377d0da5 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c @@ -20,15 +20,16 @@ #include #include -DEFINE_PER_CPU(struct context_tracking, context_tracking) = { -#ifdef CONFIG_CONTEXT_TRACKING_FORCE - .active = true, -#endif -}; +struct static_key context_tracking_enabled = STATIC_KEY_INIT_FALSE; + +DEFINE_PER_CPU(struct context_tracking, context_tracking); void context_tracking_cpu_set(int cpu) { - per_cpu(context_tracking.active, cpu) = true; + if (!per_cpu(context_tracking.active, cpu)) { + per_cpu(context_tracking.active, cpu) = true; + static_key_slow_inc(&context_tracking_enabled); + } } /** @@ -202,3 +203,13 @@ void context_tracking_task_switch(struct task_struct *prev, clear_tsk_thread_flag(prev, TIF_NOHZ); set_tsk_thread_flag(next, TIF_NOHZ); } + +#ifdef CONFIG_CONTEXT_TRACKING_FORCE +void __init context_tracking_init(void) +{ + int cpu; + + for_each_possible_cpu(cpu) + context_tracking_cpu_set(cpu); +} +#endif -- cgit v1.2.3-71-gd317 From ad65782fba507d91a0a98f519b59e79cac1b474c Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 10 Jul 2013 02:44:35 +0200 Subject: context_tracking: Optimize main APIs off case with static key Optimize user and exception entry/exit APIs with static keys. This minimize the overhead for those who enable CONFIG_NO_HZ_FULL without always using it. Having no range passed to nohz_full= should result in the probes to be nopped (at least we hope so...). If this proves not be enough in the long term, we'll need to bring an exception slow path by re-routing the exception handlers. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman --- include/linux/context_tracking.h | 27 ++++++++++++++++++++++----- kernel/context_tracking.c | 12 ++++++------ 2 files changed, 28 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index c138c24bad1a..38ab60b3f3a6 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -38,23 +38,40 @@ static inline bool context_tracking_active(void) extern void context_tracking_cpu_set(int cpu); -extern void user_enter(void); -extern void user_exit(void); +extern void context_tracking_user_enter(void); +extern void context_tracking_user_exit(void); + +static inline void user_enter(void) +{ + if (static_key_false(&context_tracking_enabled)) + context_tracking_user_enter(); + +} +static inline void user_exit(void) +{ + if (static_key_false(&context_tracking_enabled)) + context_tracking_user_exit(); +} static inline enum ctx_state exception_enter(void) { enum ctx_state prev_ctx; + if (!static_key_false(&context_tracking_enabled)) + return 0; + prev_ctx = this_cpu_read(context_tracking.state); - user_exit(); + context_tracking_user_exit(); return prev_ctx; } static inline void exception_exit(enum ctx_state prev_ctx) { - if (prev_ctx == IN_USER) - user_enter(); + if (static_key_false(&context_tracking_enabled)) { + if (prev_ctx == IN_USER) + context_tracking_user_enter(); + } } extern void context_tracking_task_switch(struct task_struct *prev, diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index 839d377d0da5..6e89e094c80e 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c @@ -33,15 +33,15 @@ void context_tracking_cpu_set(int cpu) } /** - * user_enter - Inform the context tracking that the CPU is going to - * enter userspace mode. + * context_tracking_user_enter - Inform the context tracking that the CPU is going to + * enter userspace mode. * * This function must be called right before we switch from the kernel * to userspace, when it's guaranteed the remaining kernel instructions * to execute won't use any RCU read side critical section because this * function sets RCU in extended quiescent state. */ -void user_enter(void) +void context_tracking_user_enter(void) { unsigned long flags; @@ -131,8 +131,8 @@ EXPORT_SYMBOL_GPL(preempt_schedule_context); #endif /* CONFIG_PREEMPT */ /** - * user_exit - Inform the context tracking that the CPU is - * exiting userspace mode and entering the kernel. + * context_tracking_user_exit - Inform the context tracking that the CPU is + * exiting userspace mode and entering the kernel. * * This function must be called after we entered the kernel from userspace * before any use of RCU read side critical section. This potentially include @@ -141,7 +141,7 @@ EXPORT_SYMBOL_GPL(preempt_schedule_context); * This call supports re-entrancy. This way it can be called from any exception * handler without needing to know if we came from userspace or not. */ -void user_exit(void) +void context_tracking_user_exit(void) { unsigned long flags; -- cgit v1.2.3-71-gd317 From 48d6a816a8bf36e2a197c322697323003bdc1cfe Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 10 Jul 2013 02:44:35 +0200 Subject: context_tracking: Optimize guest APIs off case with static key Optimize guest entry/exit APIs with static keys. This minimize the overhead for those who enable CONFIG_NO_HZ_FULL without always using it. Having no range passed to nohz_full= should result in the probes overhead to be minimized. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman --- include/linux/context_tracking.h | 19 +++++++++++++++++-- kernel/context_tracking.c | 23 ++--------------------- kernel/sched/cputime.c | 2 ++ 3 files changed, 21 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index 38ab60b3f3a6..8854eadb2142 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -95,8 +95,23 @@ static inline void context_tracking_init(void) { } #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN -extern void guest_enter(void); -extern void guest_exit(void); +static inline void guest_enter(void) +{ + if (static_key_false(&context_tracking_enabled) && + vtime_accounting_enabled()) + vtime_guest_enter(current); + else + current->flags |= PF_VCPU; +} + +static inline void guest_exit(void) +{ + if (static_key_false(&context_tracking_enabled) && + vtime_accounting_enabled()) + vtime_guest_exit(current); + else + current->flags &= ~PF_VCPU; +} #else static inline void guest_enter(void) { diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index 6e89e094c80e..b6a186c4b886 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c @@ -21,8 +21,10 @@ #include struct static_key context_tracking_enabled = STATIC_KEY_INIT_FALSE; +EXPORT_SYMBOL_GPL(context_tracking_enabled); DEFINE_PER_CPU(struct context_tracking, context_tracking); +EXPORT_SYMBOL_GPL(context_tracking); void context_tracking_cpu_set(int cpu) { @@ -163,27 +165,6 @@ void context_tracking_user_exit(void) local_irq_restore(flags); } -#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN -void guest_enter(void) -{ - if (vtime_accounting_enabled()) - vtime_guest_enter(current); - else - current->flags |= PF_VCPU; -} -EXPORT_SYMBOL_GPL(guest_enter); - -void guest_exit(void) -{ - if (vtime_accounting_enabled()) - vtime_guest_exit(current); - else - current->flags &= ~PF_VCPU; -} -EXPORT_SYMBOL_GPL(guest_exit); -#endif /* CONFIG_VIRT_CPU_ACCOUNTING_GEN */ - - /** * context_tracking_task_switch - context switch the syscall callbacks * @prev: the task that is being switched out diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index 223a35efa0a6..bb6b29a3067c 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -724,6 +724,7 @@ void vtime_guest_enter(struct task_struct *tsk) current->flags |= PF_VCPU; write_sequnlock(&tsk->vtime_seqlock); } +EXPORT_SYMBOL_GPL(vtime_guest_enter); void vtime_guest_exit(struct task_struct *tsk) { @@ -732,6 +733,7 @@ void vtime_guest_exit(struct task_struct *tsk) current->flags &= ~PF_VCPU; write_sequnlock(&tsk->vtime_seqlock); } +EXPORT_SYMBOL_GPL(vtime_guest_exit); void vtime_account_idle(struct task_struct *tsk) { -- cgit v1.2.3-71-gd317 From 73d424f9af7b571276e6284617cb59726d47bf12 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 11 Jul 2013 19:42:13 +0200 Subject: context_tracking: Optimize context switch off case with static keys No need for syscall slowpath if no CPU is full dynticks, rather nop this in this case. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman --- include/linux/context_tracking.h | 11 +++++++++-- kernel/context_tracking.c | 6 +++--- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index 8854eadb2142..e070ea5dadac 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -40,6 +40,8 @@ extern void context_tracking_cpu_set(int cpu); extern void context_tracking_user_enter(void); extern void context_tracking_user_exit(void); +extern void __context_tracking_task_switch(struct task_struct *prev, + struct task_struct *next); static inline void user_enter(void) { @@ -74,8 +76,12 @@ static inline void exception_exit(enum ctx_state prev_ctx) } } -extern void context_tracking_task_switch(struct task_struct *prev, - struct task_struct *next); +static inline void context_tracking_task_switch(struct task_struct *prev, + struct task_struct *next) +{ + if (static_key_false(&context_tracking_enabled)) + __context_tracking_task_switch(prev, next); +} #else static inline bool context_tracking_in_user(void) { return false; } static inline void user_enter(void) { } @@ -112,6 +118,7 @@ static inline void guest_exit(void) else current->flags &= ~PF_VCPU; } + #else static inline void guest_enter(void) { diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index b6a186c4b886..c17822673c39 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c @@ -166,7 +166,7 @@ void context_tracking_user_exit(void) } /** - * context_tracking_task_switch - context switch the syscall callbacks + * __context_tracking_task_switch - context switch the syscall callbacks * @prev: the task that is being switched out * @next: the task that is being switched in * @@ -178,8 +178,8 @@ void context_tracking_user_exit(void) * migrate to some CPU that doesn't do the context tracking. As such the TIF * flag may not be desired there. */ -void context_tracking_task_switch(struct task_struct *prev, - struct task_struct *next) +void __context_tracking_task_switch(struct task_struct *prev, + struct task_struct *next) { clear_tsk_thread_flag(prev, TIF_NOHZ); set_tsk_thread_flag(next, TIF_NOHZ); -- cgit v1.2.3-71-gd317 From 1b6a259aa5ab16d8b215bfc19ff7c9ffa8858f10 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Thu, 11 Jul 2013 20:27:43 +0200 Subject: context_tracking: User/kernel broundary cross trace events This can be useful to track all kernel/user round trips. And it's also helpful to debug the context tracking subsystem. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman --- include/trace/events/context_tracking.h | 58 +++++++++++++++++++++++++++++++++ kernel/context_tracking.c | 5 +++ 2 files changed, 63 insertions(+) create mode 100644 include/trace/events/context_tracking.h (limited to 'include') diff --git a/include/trace/events/context_tracking.h b/include/trace/events/context_tracking.h new file mode 100644 index 000000000000..ce8007cf29cf --- /dev/null +++ b/include/trace/events/context_tracking.h @@ -0,0 +1,58 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM context_tracking + +#if !defined(_TRACE_CONTEXT_TRACKING_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_CONTEXT_TRACKING_H + +#include + +DECLARE_EVENT_CLASS(context_tracking_user, + + TP_PROTO(int dummy), + + TP_ARGS(dummy), + + TP_STRUCT__entry( + __field( int, dummy ) + ), + + TP_fast_assign( + __entry->dummy = dummy; + ), + + TP_printk("%s", "") +); + +/** + * user_enter - called when the kernel resumes to userspace + * @dummy: dummy arg to make trace event macro happy + * + * This event occurs when the kernel resumes to userspace after + * an exception or a syscall. + */ +DEFINE_EVENT(context_tracking_user, user_enter, + + TP_PROTO(int dummy), + + TP_ARGS(dummy) +); + +/** + * user_exit - called when userspace enters the kernel + * @dummy: dummy arg to make trace event macro happy + * + * This event occurs when userspace enters the kernel through + * an exception or a syscall. + */ +DEFINE_EVENT(context_tracking_user, user_exit, + + TP_PROTO(int dummy), + + TP_ARGS(dummy) +); + + +#endif /* _TRACE_CONTEXT_TRACKING_H */ + +/* This part must be outside protection */ +#include diff --git a/kernel/context_tracking.c b/kernel/context_tracking.c index c17822673c39..247091bf0587 100644 --- a/kernel/context_tracking.c +++ b/kernel/context_tracking.c @@ -20,6 +20,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + struct static_key context_tracking_enabled = STATIC_KEY_INIT_FALSE; EXPORT_SYMBOL_GPL(context_tracking_enabled); @@ -64,6 +67,7 @@ void context_tracking_user_enter(void) local_irq_save(flags); if ( __this_cpu_read(context_tracking.state) != IN_USER) { if (__this_cpu_read(context_tracking.active)) { + trace_user_enter(0); /* * At this stage, only low level arch entry code remains and * then we'll run in userspace. We can assume there won't be @@ -159,6 +163,7 @@ void context_tracking_user_exit(void) */ rcu_user_exit(); vtime_user_exit(current); + trace_user_exit(0); } __this_cpu_write(context_tracking.state, IN_KERNEL); } -- cgit v1.2.3-71-gd317 From e7358b3bc0d7fcec0e7a724477f06db2b0c68e21 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 12 Jul 2013 02:15:49 +0200 Subject: context_tracking: Split low level state headers We plan to use the context tracking static key on inline vtime APIs. For this we need to include the context tracking headers from those of vtime. However vtime headers need to stay low level because they are included in hardirq.h that mostly contains standalone definitions. But context_tracking.h includes sched.h for a few task_struct references, therefore it wouldn't be sensible to include it from vtime.h To solve this, lets split the context tracking headers and move out the pure state definitions that only require a few low level headers. We can safely include that small part in vtime.h later. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman --- include/linux/context_tracking.h | 31 +-------------------------- include/linux/context_tracking_state.h | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 30 deletions(-) create mode 100644 include/linux/context_tracking_state.h (limited to 'include') diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index e070ea5dadac..82ec4870e064 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -2,40 +2,12 @@ #define _LINUX_CONTEXT_TRACKING_H #include -#include #include -#include +#include #include -struct context_tracking { - /* - * When active is false, probes are unset in order - * to minimize overhead: TIF flags are cleared - * and calls to user_enter/exit are ignored. This - * may be further optimized using static keys. - */ - bool active; - enum ctx_state { - IN_KERNEL = 0, - IN_USER, - } state; -}; - #ifdef CONFIG_CONTEXT_TRACKING -extern struct static_key context_tracking_enabled; -DECLARE_PER_CPU(struct context_tracking, context_tracking); - -static inline bool context_tracking_in_user(void) -{ - return __this_cpu_read(context_tracking.state) == IN_USER; -} - -static inline bool context_tracking_active(void) -{ - return __this_cpu_read(context_tracking.active); -} - extern void context_tracking_cpu_set(int cpu); extern void context_tracking_user_enter(void); @@ -83,7 +55,6 @@ static inline void context_tracking_task_switch(struct task_struct *prev, __context_tracking_task_switch(prev, next); } #else -static inline bool context_tracking_in_user(void) { return false; } static inline void user_enter(void) { } static inline void user_exit(void) { } static inline enum ctx_state exception_enter(void) { return 0; } diff --git a/include/linux/context_tracking_state.h b/include/linux/context_tracking_state.h new file mode 100644 index 000000000000..0f1979d0674f --- /dev/null +++ b/include/linux/context_tracking_state.h @@ -0,0 +1,39 @@ +#ifndef _LINUX_CONTEXT_TRACKING_STATE_H +#define _LINUX_CONTEXT_TRACKING_STATE_H + +#include +#include + +struct context_tracking { + /* + * When active is false, probes are unset in order + * to minimize overhead: TIF flags are cleared + * and calls to user_enter/exit are ignored. This + * may be further optimized using static keys. + */ + bool active; + enum ctx_state { + IN_KERNEL = 0, + IN_USER, + } state; +}; + +#ifdef CONFIG_CONTEXT_TRACKING +extern struct static_key context_tracking_enabled; +DECLARE_PER_CPU(struct context_tracking, context_tracking); + +static inline bool context_tracking_in_user(void) +{ + return __this_cpu_read(context_tracking.state) == IN_USER; +} + +static inline bool context_tracking_active(void) +{ + return __this_cpu_read(context_tracking.active); +} +#else +static inline bool context_tracking_in_user(void) { return false; } +static inline bool context_tracking_active(void) { return false; } +#endif /* CONFIG_CONTEXT_TRACKING */ + +#endif -- cgit v1.2.3-71-gd317 From 2d4b84739f0acf61f37267b4e80c5bd85fb90a7e Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Mon, 29 Jul 2013 20:29:43 +0200 Subject: hardirq: Split preempt count mask definitions In order to use static keys with vtime APIs, we'll need to add static keys headers to vtime.h hardirq.h then becomes a problem because it needs vtime.h for irqtime accounting in irq_enter/irq_exit, but it's often included just to get the irq mask definitions in the task preempt_count field and the APIs that come along: in_interrupt(), in_hardirq(), etc... Some very low level arch headers sometimes need these masks and APIs such as arch/m68k/include/asm/irqflags.h for example. But they don't want to include hardirq.h if vtime.h, jump_label.h and even workqueue.h come along. Including such bloated high level header from arch headers can quickly result in circular headers dependency that crash the build. So let's split hardirq.h in two parts: * preempt_mask.h that gathers all the preempt_count definitions and the APIs associated. This one is considered low level and can be safely included anywhere. * hardirq.h that includes the previous one. It defines the irq entry/exit APIs. To avoid future circular headers dependencies, the preempt_mask.h inclusion can replace hardirq.h on files that don't implement irq low level handlers but just need the atomic/context check APIs. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman --- include/linux/hardirq.h | 117 +---------------------------------------- include/linux/preempt_mask.h | 122 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 116 deletions(-) create mode 100644 include/linux/preempt_mask.h (limited to 'include') diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h index 05bcc0903766..ccfe17c5c8da 100644 --- a/include/linux/hardirq.h +++ b/include/linux/hardirq.h @@ -1,126 +1,11 @@ #ifndef LINUX_HARDIRQ_H #define LINUX_HARDIRQ_H -#include +#include #include #include #include -#include -/* - * We put the hardirq and softirq counter into the preemption - * counter. The bitmask has the following meaning: - * - * - bits 0-7 are the preemption count (max preemption depth: 256) - * - bits 8-15 are the softirq count (max # of softirqs: 256) - * - * The hardirq count can in theory reach the same as NR_IRQS. - * In reality, the number of nested IRQS is limited to the stack - * size as well. For archs with over 1000 IRQS it is not practical - * to expect that they will all nest. We give a max of 10 bits for - * hardirq nesting. An arch may choose to give less than 10 bits. - * m68k expects it to be 8. - * - * - bits 16-25 are the hardirq count (max # of nested hardirqs: 1024) - * - bit 26 is the NMI_MASK - * - bit 27 is the PREEMPT_ACTIVE flag - * - * PREEMPT_MASK: 0x000000ff - * SOFTIRQ_MASK: 0x0000ff00 - * HARDIRQ_MASK: 0x03ff0000 - * NMI_MASK: 0x04000000 - */ -#define PREEMPT_BITS 8 -#define SOFTIRQ_BITS 8 -#define NMI_BITS 1 - -#define MAX_HARDIRQ_BITS 10 - -#ifndef HARDIRQ_BITS -# define HARDIRQ_BITS MAX_HARDIRQ_BITS -#endif - -#if HARDIRQ_BITS > MAX_HARDIRQ_BITS -#error HARDIRQ_BITS too high! -#endif - -#define PREEMPT_SHIFT 0 -#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) -#define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) -#define NMI_SHIFT (HARDIRQ_SHIFT + HARDIRQ_BITS) - -#define __IRQ_MASK(x) ((1UL << (x))-1) - -#define PREEMPT_MASK (__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT) -#define SOFTIRQ_MASK (__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) -#define HARDIRQ_MASK (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) -#define NMI_MASK (__IRQ_MASK(NMI_BITS) << NMI_SHIFT) - -#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) -#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) -#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) -#define NMI_OFFSET (1UL << NMI_SHIFT) - -#define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET) - -#ifndef PREEMPT_ACTIVE -#define PREEMPT_ACTIVE_BITS 1 -#define PREEMPT_ACTIVE_SHIFT (NMI_SHIFT + NMI_BITS) -#define PREEMPT_ACTIVE (__IRQ_MASK(PREEMPT_ACTIVE_BITS) << PREEMPT_ACTIVE_SHIFT) -#endif - -#if PREEMPT_ACTIVE < (1 << (NMI_SHIFT + NMI_BITS)) -#error PREEMPT_ACTIVE is too low! -#endif - -#define hardirq_count() (preempt_count() & HARDIRQ_MASK) -#define softirq_count() (preempt_count() & SOFTIRQ_MASK) -#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \ - | NMI_MASK)) - -/* - * Are we doing bottom half or hardware interrupt processing? - * Are we in a softirq context? Interrupt context? - * in_softirq - Are we currently processing softirq or have bh disabled? - * in_serving_softirq - Are we currently processing softirq? - */ -#define in_irq() (hardirq_count()) -#define in_softirq() (softirq_count()) -#define in_interrupt() (irq_count()) -#define in_serving_softirq() (softirq_count() & SOFTIRQ_OFFSET) - -/* - * Are we in NMI context? - */ -#define in_nmi() (preempt_count() & NMI_MASK) - -#if defined(CONFIG_PREEMPT_COUNT) -# define PREEMPT_CHECK_OFFSET 1 -#else -# define PREEMPT_CHECK_OFFSET 0 -#endif - -/* - * Are we running in atomic context? WARNING: this macro cannot - * always detect atomic context; in particular, it cannot know about - * held spinlocks in non-preemptible kernels. Thus it should not be - * used in the general case to determine whether sleeping is possible. - * Do not use in_atomic() in driver code. - */ -#define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != 0) - -/* - * Check whether we were atomic before we did preempt_disable(): - * (used by the scheduler, *after* releasing the kernel lock) - */ -#define in_atomic_preempt_off() \ - ((preempt_count() & ~PREEMPT_ACTIVE) != PREEMPT_CHECK_OFFSET) - -#ifdef CONFIG_PREEMPT_COUNT -# define preemptible() (preempt_count() == 0 && !irqs_disabled()) -#else -# define preemptible() 0 -#endif #if defined(CONFIG_SMP) || defined(CONFIG_GENERIC_HARDIRQS) extern void synchronize_irq(unsigned int irq); diff --git a/include/linux/preempt_mask.h b/include/linux/preempt_mask.h new file mode 100644 index 000000000000..931bc616219f --- /dev/null +++ b/include/linux/preempt_mask.h @@ -0,0 +1,122 @@ +#ifndef LINUX_PREEMPT_MASK_H +#define LINUX_PREEMPT_MASK_H + +#include +#include + +/* + * We put the hardirq and softirq counter into the preemption + * counter. The bitmask has the following meaning: + * + * - bits 0-7 are the preemption count (max preemption depth: 256) + * - bits 8-15 are the softirq count (max # of softirqs: 256) + * + * The hardirq count can in theory reach the same as NR_IRQS. + * In reality, the number of nested IRQS is limited to the stack + * size as well. For archs with over 1000 IRQS it is not practical + * to expect that they will all nest. We give a max of 10 bits for + * hardirq nesting. An arch may choose to give less than 10 bits. + * m68k expects it to be 8. + * + * - bits 16-25 are the hardirq count (max # of nested hardirqs: 1024) + * - bit 26 is the NMI_MASK + * - bit 27 is the PREEMPT_ACTIVE flag + * + * PREEMPT_MASK: 0x000000ff + * SOFTIRQ_MASK: 0x0000ff00 + * HARDIRQ_MASK: 0x03ff0000 + * NMI_MASK: 0x04000000 + */ +#define PREEMPT_BITS 8 +#define SOFTIRQ_BITS 8 +#define NMI_BITS 1 + +#define MAX_HARDIRQ_BITS 10 + +#ifndef HARDIRQ_BITS +# define HARDIRQ_BITS MAX_HARDIRQ_BITS +#endif + +#if HARDIRQ_BITS > MAX_HARDIRQ_BITS +#error HARDIRQ_BITS too high! +#endif + +#define PREEMPT_SHIFT 0 +#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS) +#define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS) +#define NMI_SHIFT (HARDIRQ_SHIFT + HARDIRQ_BITS) + +#define __IRQ_MASK(x) ((1UL << (x))-1) + +#define PREEMPT_MASK (__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT) +#define SOFTIRQ_MASK (__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) +#define HARDIRQ_MASK (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) +#define NMI_MASK (__IRQ_MASK(NMI_BITS) << NMI_SHIFT) + +#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT) +#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT) +#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT) +#define NMI_OFFSET (1UL << NMI_SHIFT) + +#define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET) + +#ifndef PREEMPT_ACTIVE +#define PREEMPT_ACTIVE_BITS 1 +#define PREEMPT_ACTIVE_SHIFT (NMI_SHIFT + NMI_BITS) +#define PREEMPT_ACTIVE (__IRQ_MASK(PREEMPT_ACTIVE_BITS) << PREEMPT_ACTIVE_SHIFT) +#endif + +#if PREEMPT_ACTIVE < (1 << (NMI_SHIFT + NMI_BITS)) +#error PREEMPT_ACTIVE is too low! +#endif + +#define hardirq_count() (preempt_count() & HARDIRQ_MASK) +#define softirq_count() (preempt_count() & SOFTIRQ_MASK) +#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \ + | NMI_MASK)) + +/* + * Are we doing bottom half or hardware interrupt processing? + * Are we in a softirq context? Interrupt context? + * in_softirq - Are we currently processing softirq or have bh disabled? + * in_serving_softirq - Are we currently processing softirq? + */ +#define in_irq() (hardirq_count()) +#define in_softirq() (softirq_count()) +#define in_interrupt() (irq_count()) +#define in_serving_softirq() (softirq_count() & SOFTIRQ_OFFSET) + +/* + * Are we in NMI context? + */ +#define in_nmi() (preempt_count() & NMI_MASK) + +#if defined(CONFIG_PREEMPT_COUNT) +# define PREEMPT_CHECK_OFFSET 1 +#else +# define PREEMPT_CHECK_OFFSET 0 +#endif + +/* + * Are we running in atomic context? WARNING: this macro cannot + * always detect atomic context; in particular, it cannot know about + * held spinlocks in non-preemptible kernels. Thus it should not be + * used in the general case to determine whether sleeping is possible. + * Do not use in_atomic() in driver code. + */ +#define in_atomic() ((preempt_count() & ~PREEMPT_ACTIVE) != 0) + +/* + * Check whether we were atomic before we did preempt_disable(): + * (used by the scheduler, *after* releasing the kernel lock) + */ +#define in_atomic_preempt_off() \ + ((preempt_count() & ~PREEMPT_ACTIVE) != PREEMPT_CHECK_OFFSET) + +#ifdef CONFIG_PREEMPT_COUNT +# define preemptible() (preempt_count() == 0 && !irqs_disabled()) +#else +# define preemptible() 0 +#endif + +#endif /* LINUX_PREEMPT_MASK_H */ -- cgit v1.2.3-71-gd317 From a5725ac23bf4ff79656ac2c317c323e261327fb3 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Tue, 16 Jul 2013 18:50:52 +0200 Subject: vtime: Describe overriden functions in dedicated arch headers If the arch overrides some generic vtime APIs, let it describe these on a dedicated and standalone header. This way it becomes convenient to include it in vtime generic headers without irrelevant stuff in such a low level header. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman Cc: Martin Schwidefsky Cc: Heiko Carstens --- arch/ia64/include/asm/Kbuild | 1 + arch/powerpc/include/asm/Kbuild | 1 + arch/s390/include/asm/cputime.h | 3 --- arch/s390/include/asm/vtime.h | 7 +++++++ arch/s390/kernel/vtime.c | 1 + include/asm-generic/vtime.h | 0 include/linux/vtime.h | 4 ++++ 7 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 arch/s390/include/asm/vtime.h create mode 100644 include/asm-generic/vtime.h (limited to 'include') diff --git a/arch/ia64/include/asm/Kbuild b/arch/ia64/include/asm/Kbuild index 05b03ecd7933..a3456f34f672 100644 --- a/arch/ia64/include/asm/Kbuild +++ b/arch/ia64/include/asm/Kbuild @@ -3,3 +3,4 @@ generic-y += clkdev.h generic-y += exec.h generic-y += kvm_para.h generic-y += trace_clock.h +generic-y += vtime.h \ No newline at end of file diff --git a/arch/powerpc/include/asm/Kbuild b/arch/powerpc/include/asm/Kbuild index 650757c300db..704e6f10ae80 100644 --- a/arch/powerpc/include/asm/Kbuild +++ b/arch/powerpc/include/asm/Kbuild @@ -2,3 +2,4 @@ generic-y += clkdev.h generic-y += rwsem.h generic-y += trace_clock.h +generic-y += vtime.h \ No newline at end of file diff --git a/arch/s390/include/asm/cputime.h b/arch/s390/include/asm/cputime.h index d2ff41370c0c..f65bd3634519 100644 --- a/arch/s390/include/asm/cputime.h +++ b/arch/s390/include/asm/cputime.h @@ -13,9 +13,6 @@ #include -#define __ARCH_HAS_VTIME_ACCOUNT -#define __ARCH_HAS_VTIME_TASK_SWITCH - /* We want to use full resolution of the CPU timer: 2**-12 micro-seconds. */ typedef unsigned long long __nocast cputime_t; diff --git a/arch/s390/include/asm/vtime.h b/arch/s390/include/asm/vtime.h new file mode 100644 index 000000000000..af9896c53eb3 --- /dev/null +++ b/arch/s390/include/asm/vtime.h @@ -0,0 +1,7 @@ +#ifndef _S390_VTIME_H +#define _S390_VTIME_H + +#define __ARCH_HAS_VTIME_ACCOUNT +#define __ARCH_HAS_VTIME_TASK_SWITCH + +#endif /* _S390_VTIME_H */ diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 9b9c1b78ec67..abcfab55f99b 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "entry.h" diff --git a/include/asm-generic/vtime.h b/include/asm-generic/vtime.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/include/linux/vtime.h b/include/linux/vtime.h index b1dd2db80076..2ad073915e8c 100644 --- a/include/linux/vtime.h +++ b/include/linux/vtime.h @@ -1,6 +1,10 @@ #ifndef _LINUX_KERNEL_VTIME_H #define _LINUX_KERNEL_VTIME_H +#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE +#include +#endif + struct task_struct; #ifdef CONFIG_VIRT_CPU_ACCOUNTING -- cgit v1.2.3-71-gd317 From b04934061330a4a449cfce703c97d887c3e11cd7 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Fri, 12 Jul 2013 03:10:15 +0200 Subject: vtime: Optimize full dynticks accounting off case with static keys If no CPU is in the full dynticks range, we can avoid the full dynticks cputime accounting through generic vtime along with its overhead and use the traditional tick based accounting instead. Let's do this and nope the off case with static keys. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman --- include/linux/context_tracking.h | 6 ++-- include/linux/vtime.h | 70 ++++++++++++++++++++++++++++++++++------ kernel/sched/cputime.c | 22 +++---------- 3 files changed, 67 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/linux/context_tracking.h b/include/linux/context_tracking.h index 82ec4870e064..158158704c30 100644 --- a/include/linux/context_tracking.h +++ b/include/linux/context_tracking.h @@ -74,8 +74,7 @@ static inline void context_tracking_init(void) { } #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN static inline void guest_enter(void) { - if (static_key_false(&context_tracking_enabled) && - vtime_accounting_enabled()) + if (vtime_accounting_enabled()) vtime_guest_enter(current); else current->flags |= PF_VCPU; @@ -83,8 +82,7 @@ static inline void guest_enter(void) static inline void guest_exit(void) { - if (static_key_false(&context_tracking_enabled) && - vtime_accounting_enabled()) + if (vtime_accounting_enabled()) vtime_guest_exit(current); else current->flags &= ~PF_VCPU; diff --git a/include/linux/vtime.h b/include/linux/vtime.h index 2ad073915e8c..f5b72b364bda 100644 --- a/include/linux/vtime.h +++ b/include/linux/vtime.h @@ -1,22 +1,68 @@ #ifndef _LINUX_KERNEL_VTIME_H #define _LINUX_KERNEL_VTIME_H +#include #ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE #include #endif + struct task_struct; +/* + * vtime_accounting_enabled() definitions/declarations + */ +#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE +static inline bool vtime_accounting_enabled(void) { return true; } +#endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */ + +#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN +static inline bool vtime_accounting_enabled(void) +{ + if (static_key_false(&context_tracking_enabled)) { + if (context_tracking_active()) + return true; + } + + return false; +} +#endif /* CONFIG_VIRT_CPU_ACCOUNTING_GEN */ + +#ifndef CONFIG_VIRT_CPU_ACCOUNTING +static inline bool vtime_accounting_enabled(void) { return false; } +#endif /* !CONFIG_VIRT_CPU_ACCOUNTING */ + + +/* + * Common vtime APIs + */ #ifdef CONFIG_VIRT_CPU_ACCOUNTING + +#ifdef __ARCH_HAS_VTIME_TASK_SWITCH extern void vtime_task_switch(struct task_struct *prev); +#else +extern void vtime_common_task_switch(struct task_struct *prev); +static inline void vtime_task_switch(struct task_struct *prev) +{ + if (vtime_accounting_enabled()) + vtime_common_task_switch(prev); +} +#endif /* __ARCH_HAS_VTIME_TASK_SWITCH */ + extern void vtime_account_system(struct task_struct *tsk); extern void vtime_account_idle(struct task_struct *tsk); extern void vtime_account_user(struct task_struct *tsk); -extern void vtime_account_irq_enter(struct task_struct *tsk); -#ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE -static inline bool vtime_accounting_enabled(void) { return true; } -#endif +#ifdef __ARCH_HAS_VTIME_ACCOUNT +extern void vtime_account_irq_enter(struct task_struct *tsk); +#else +extern void vtime_common_account_irq_enter(struct task_struct *tsk); +static inline void vtime_account_irq_enter(struct task_struct *tsk) +{ + if (vtime_accounting_enabled()) + vtime_common_account_irq_enter(tsk); +} +#endif /* __ARCH_HAS_VTIME_ACCOUNT */ #else /* !CONFIG_VIRT_CPU_ACCOUNTING */ @@ -24,14 +70,20 @@ static inline void vtime_task_switch(struct task_struct *prev) { } static inline void vtime_account_system(struct task_struct *tsk) { } static inline void vtime_account_user(struct task_struct *tsk) { } static inline void vtime_account_irq_enter(struct task_struct *tsk) { } -static inline bool vtime_accounting_enabled(void) { return false; } -#endif +#endif /* !CONFIG_VIRT_CPU_ACCOUNTING */ #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN extern void arch_vtime_task_switch(struct task_struct *tsk); -extern void vtime_account_irq_exit(struct task_struct *tsk); -extern bool vtime_accounting_enabled(void); +extern void vtime_gen_account_irq_exit(struct task_struct *tsk); + +static inline void vtime_account_irq_exit(struct task_struct *tsk) +{ + if (vtime_accounting_enabled()) + vtime_gen_account_irq_exit(tsk); +} + extern void vtime_user_enter(struct task_struct *tsk); + static inline void vtime_user_exit(struct task_struct *tsk) { vtime_account_user(tsk); @@ -39,7 +91,7 @@ static inline void vtime_user_exit(struct task_struct *tsk) extern void vtime_guest_enter(struct task_struct *tsk); extern void vtime_guest_exit(struct task_struct *tsk); extern void vtime_init_idle(struct task_struct *tsk, int cpu); -#else +#else /* !CONFIG_VIRT_CPU_ACCOUNTING_GEN */ static inline void vtime_account_irq_exit(struct task_struct *tsk) { /* On hard|softirq exit we always account to hard|softirq cputime */ diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index b62d5c027c7e..0831b06aab97 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -378,11 +378,8 @@ static inline void irqtime_account_process_tick(struct task_struct *p, int user_ #ifdef CONFIG_VIRT_CPU_ACCOUNTING #ifndef __ARCH_HAS_VTIME_TASK_SWITCH -void vtime_task_switch(struct task_struct *prev) +void vtime_common_task_switch(struct task_struct *prev) { - if (!vtime_accounting_enabled()) - return; - if (is_idle_task(prev)) vtime_account_idle(prev); else @@ -404,11 +401,8 @@ void vtime_task_switch(struct task_struct *prev) * vtime_account(). */ #ifndef __ARCH_HAS_VTIME_ACCOUNT -void vtime_account_irq_enter(struct task_struct *tsk) +void vtime_common_account_irq_enter(struct task_struct *tsk) { - if (!vtime_accounting_enabled()) - return; - if (!in_interrupt()) { /* * If we interrupted user, context_tracking_in_user() @@ -428,7 +422,7 @@ void vtime_account_irq_enter(struct task_struct *tsk) } vtime_account_system(tsk); } -EXPORT_SYMBOL_GPL(vtime_account_irq_enter); +EXPORT_SYMBOL_GPL(vtime_common_account_irq_enter); #endif /* __ARCH_HAS_VTIME_ACCOUNT */ #endif /* CONFIG_VIRT_CPU_ACCOUNTING */ @@ -669,11 +663,8 @@ void vtime_account_system(struct task_struct *tsk) write_sequnlock(&tsk->vtime_seqlock); } -void vtime_account_irq_exit(struct task_struct *tsk) +void vtime_gen_account_irq_exit(struct task_struct *tsk) { - if (!vtime_accounting_enabled()) - return; - write_seqlock(&tsk->vtime_seqlock); if (context_tracking_in_user()) tsk->vtime_snap_whence = VTIME_USER; @@ -732,11 +723,6 @@ void vtime_account_idle(struct task_struct *tsk) account_idle_time(delta_cpu); } -bool vtime_accounting_enabled(void) -{ - return context_tracking_active(); -} - void arch_vtime_task_switch(struct task_struct *prev) { write_seqlock(&prev->vtime_seqlock); -- cgit v1.2.3-71-gd317 From 460775df4680b4593d8449bc171008578625a850 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 24 Jul 2013 23:52:27 +0200 Subject: nohz: Optimize full dynticks state checks with static keys These APIs are frequenctly accessed and priority is given to optimize the full dynticks off-case in order to let distros enable this feature without suffering from significant performance regressions. Let's inline these APIs and optimize them with static keys. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman --- include/linux/tick.h | 25 +++++++++++++++++++++++-- kernel/time/tick-sched.c | 14 ++------------ 2 files changed, 25 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/tick.h b/include/linux/tick.h index 9180f4b85e6d..c60b079e1b37 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -10,6 +10,8 @@ #include #include #include +#include +#include #ifdef CONFIG_GENERIC_CLOCKEVENTS @@ -158,15 +160,34 @@ static inline u64 get_cpu_iowait_time_us(int cpu, u64 *unused) { return -1; } # endif /* !CONFIG_NO_HZ_COMMON */ #ifdef CONFIG_NO_HZ_FULL +extern bool tick_nohz_full_running; +extern cpumask_var_t tick_nohz_full_mask; + +static inline bool tick_nohz_full_enabled(void) +{ + if (!static_key_false(&context_tracking_enabled)) + return false; + + return tick_nohz_full_running; +} + +static inline bool tick_nohz_full_cpu(int cpu) +{ + if (!tick_nohz_full_enabled()) + return false; + + return cpumask_test_cpu(cpu, tick_nohz_full_mask); +} + extern void tick_nohz_init(void); -extern int tick_nohz_full_cpu(int cpu); extern void tick_nohz_full_check(void); extern void tick_nohz_full_kick(void); extern void tick_nohz_full_kick_all(void); extern void tick_nohz_task_switch(struct task_struct *tsk); #else static inline void tick_nohz_init(void) { } -static inline int tick_nohz_full_cpu(int cpu) { return 0; } +static inline bool tick_nohz_full_enabled(void) { return false; } +static inline bool tick_nohz_full_cpu(int cpu) { return false; } static inline void tick_nohz_full_check(void) { } static inline void tick_nohz_full_kick(void) { } static inline void tick_nohz_full_kick_all(void) { } diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index b28dee43e644..0b7887389bd2 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -149,7 +149,7 @@ static void tick_sched_handle(struct tick_sched *ts, struct pt_regs *regs) } #ifdef CONFIG_NO_HZ_FULL -static cpumask_var_t tick_nohz_full_mask; +cpumask_var_t tick_nohz_full_mask; bool tick_nohz_full_running; static bool can_stop_full_tick(void) @@ -270,14 +270,6 @@ out: local_irq_restore(flags); } -int tick_nohz_full_cpu(int cpu) -{ - if (!tick_nohz_full_running) - return 0; - - return cpumask_test_cpu(cpu, tick_nohz_full_mask); -} - /* Parse the boot-time nohz CPU list from the kernel parameters. */ static int __init tick_nohz_full_setup(char *str) { @@ -359,8 +351,6 @@ void __init tick_nohz_init(void) cpulist_scnprintf(nohz_full_buf, sizeof(nohz_full_buf), tick_nohz_full_mask); pr_info("NO_HZ: Full dynticks CPUs: %s.\n", nohz_full_buf); } -#else -#define tick_nohz_full_running (0) #endif /* @@ -738,7 +728,7 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts) return false; } - if (tick_nohz_full_running) { + if (tick_nohz_full_enabled()) { /* * Keep the tick alive to guarantee timekeeping progression * if there are full dynticks CPUs around -- cgit v1.2.3-71-gd317 From d13508f9440e46dccac6a2dd48d51a73b2207482 Mon Sep 17 00:00:00 2001 From: Frederic Weisbecker Date: Wed, 24 Jul 2013 23:52:27 +0200 Subject: nohz: Optimize full dynticks's sched hooks with static keys Scheduler IPIs and task context switches are serious fast path. Let's try to hide as much as we can the impact of full dynticks APIs' off case that are called on these sites through the use of static keys. Signed-off-by: Frederic Weisbecker Cc: Steven Rostedt Cc: Paul E. McKenney Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Borislav Petkov Cc: Li Zhong Cc: Mike Galbraith Cc: Kevin Hilman --- include/linux/tick.h | 20 ++++++++++++++++---- kernel/time/tick-sched.c | 8 ++++---- 2 files changed, 20 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/tick.h b/include/linux/tick.h index c60b079e1b37..a7ef1d6fceb6 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -180,20 +180,32 @@ static inline bool tick_nohz_full_cpu(int cpu) } extern void tick_nohz_init(void); -extern void tick_nohz_full_check(void); +extern void __tick_nohz_full_check(void); extern void tick_nohz_full_kick(void); extern void tick_nohz_full_kick_all(void); -extern void tick_nohz_task_switch(struct task_struct *tsk); +extern void __tick_nohz_task_switch(struct task_struct *tsk); #else static inline void tick_nohz_init(void) { } static inline bool tick_nohz_full_enabled(void) { return false; } static inline bool tick_nohz_full_cpu(int cpu) { return false; } -static inline void tick_nohz_full_check(void) { } +static inline void __tick_nohz_full_check(void) { } static inline void tick_nohz_full_kick(void) { } static inline void tick_nohz_full_kick_all(void) { } -static inline void tick_nohz_task_switch(struct task_struct *tsk) { } +static inline void __tick_nohz_task_switch(struct task_struct *tsk) { } #endif +static inline void tick_nohz_full_check(void) +{ + if (tick_nohz_full_enabled()) + __tick_nohz_full_check(); +} + +static inline void tick_nohz_task_switch(struct task_struct *tsk) +{ + if (tick_nohz_full_enabled()) + __tick_nohz_task_switch(tsk); +} + # ifdef CONFIG_CPU_IDLE_GOV_MENU extern void menu_hrtimer_cancel(void); diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 0b7887389bd2..0ff6ae710161 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -198,7 +198,7 @@ static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now); * Re-evaluate the need for the tick on the current CPU * and restart it if necessary. */ -void tick_nohz_full_check(void) +void __tick_nohz_full_check(void) { struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched); @@ -212,7 +212,7 @@ void tick_nohz_full_check(void) static void nohz_full_kick_work_func(struct irq_work *work) { - tick_nohz_full_check(); + __tick_nohz_full_check(); } static DEFINE_PER_CPU(struct irq_work, nohz_full_kick_work) = { @@ -231,7 +231,7 @@ void tick_nohz_full_kick(void) static void nohz_full_kick_ipi(void *info) { - tick_nohz_full_check(); + __tick_nohz_full_check(); } /* @@ -254,7 +254,7 @@ void tick_nohz_full_kick_all(void) * It might need the tick due to per task/process properties: * perf events, posix cpu timers, ... */ -void tick_nohz_task_switch(struct task_struct *tsk) +void __tick_nohz_task_switch(struct task_struct *tsk) { unsigned long flags; -- cgit v1.2.3-71-gd317 From b81eec09a484c588ead035003ce7555ca8a9963a Mon Sep 17 00:00:00 2001 From: Bill Huang Date: Thu, 8 Aug 2013 04:45:05 -0700 Subject: mfd: palmas: Add power off control Hook up "pm_power_off" to palmas power off routine if there is DT property "ti,system-power-controller" defined, so platform which is powered by this regulator can be powered off properly. Acked-by: Nishanth Menon Signed-off-by: Mallikarjun Kasoju Signed-off-by: Bill Huang Signed-off-by: Lee Jones --- .../devicetree/bindings/regulator/palmas-pmic.txt | 5 ++++ drivers/mfd/palmas.c | 33 ++++++++++++++++++++-- include/linux/mfd/palmas.h | 1 + 3 files changed, 37 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/regulator/palmas-pmic.txt b/Documentation/devicetree/bindings/regulator/palmas-pmic.txt index d5a308629c57..02f4c0eb601e 100644 --- a/Documentation/devicetree/bindings/regulator/palmas-pmic.txt +++ b/Documentation/devicetree/bindings/regulator/palmas-pmic.txt @@ -37,6 +37,9 @@ Optional nodes: ti,smps-range - OTP has the wrong range set for the hardware so override 0 - low range, 1 - high range. +- ti,system-power-controller: Telling whether or not this pmic is controlling + the system power. + Example: #include @@ -49,6 +52,8 @@ pmic { ti,ldo6-vibrator; + ti,system-power-controller; + regulators { smps12_reg : smps12 { regulator-name = "smps12"; diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index e4d1c706df8b..220a34d71f8d 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -229,6 +229,32 @@ static void palmas_dt_to_pdata(struct i2c_client *i2c, PALMAS_POWER_CTRL_ENABLE2_MASK; if (i2c->irq) palmas_set_pdata_irq_flag(i2c, pdata); + + pdata->pm_off = of_property_read_bool(node, + "ti,system-power-controller"); +} + +static struct palmas *palmas_dev; +static void palmas_power_off(void) +{ + unsigned int addr; + int ret, slave; + + if (!palmas_dev) + return; + + slave = PALMAS_BASE_TO_SLAVE(PALMAS_PMU_CONTROL_BASE); + addr = PALMAS_BASE_TO_REG(PALMAS_PMU_CONTROL_BASE, PALMAS_DEV_CTRL); + + ret = regmap_update_bits( + palmas_dev->regmap[slave], + addr, + PALMAS_DEV_CTRL_DEV_ON, + 0); + + if (ret) + pr_err("%s: Unable to write to DEV_CTRL_DEV_ON: %d\n", + __func__, ret); } static unsigned int palmas_features = PALMAS_PMIC_FEATURE_SMPS10_BOOST; @@ -423,10 +449,13 @@ no_irq: */ if (node) { ret = of_platform_populate(node, NULL, NULL, &i2c->dev); - if (ret < 0) + if (ret < 0) { goto err_irq; - else + } else if (pdata->pm_off && !pm_power_off) { + palmas_dev = palmas; + pm_power_off = palmas_power_off; return ret; + } } return ret; diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 1a8dd7afe084..061cce0b119a 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -258,6 +258,7 @@ struct palmas_platform_data { */ int mux_from_pdata; u8 pad1, pad2; + bool pm_off; struct palmas_pmic_platform_data *pmic_pdata; struct palmas_gpadc_platform_data *gpadc_pdata; -- cgit v1.2.3-71-gd317 From 6446221c14ef3bf58754cf1948631128dbe62700 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:37:18 +0200 Subject: ARM: pxa: ssp: add pxa_ssp_request_of() Add a function to lookup ssp devices from device tree. This way, users can reference the ssp devices in order to register to them. Signed-off-by: Daniel Mack Acked-by: Haojian Zhuang Signed-off-by: Mark Brown --- arch/arm/plat-pxa/ssp.c | 25 +++++++++++++++++++++++++ include/linux/pxa2xx_ssp.h | 11 +++++++++++ 2 files changed, 36 insertions(+) (limited to 'include') diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c index f2661355fa4e..c83f27b6bdda 100644 --- a/arch/arm/plat-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c @@ -62,6 +62,30 @@ struct ssp_device *pxa_ssp_request(int port, const char *label) } EXPORT_SYMBOL(pxa_ssp_request); +struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node, + const char *label) +{ + struct ssp_device *ssp = NULL; + + mutex_lock(&ssp_lock); + + list_for_each_entry(ssp, &ssp_list, node) { + if (ssp->of_node == of_node && ssp->use_count == 0) { + ssp->use_count++; + ssp->label = label; + break; + } + } + + mutex_unlock(&ssp_lock); + + if (&ssp->node == &ssp_list) + return NULL; + + return ssp; +} +EXPORT_SYMBOL(pxa_ssp_request_of); + void pxa_ssp_free(struct ssp_device *ssp) { mutex_lock(&ssp_lock); @@ -185,6 +209,7 @@ static int pxa_ssp_probe(struct platform_device *pdev) } ssp->use_count = 0; + ssp->of_node = dev->of_node; mutex_lock(&ssp_lock); list_add(&ssp->node, &ssp_list); diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index 467cc6307b62..49444203328a 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -21,6 +21,8 @@ #include #include +#include + /* * SSP Serial Port Registers @@ -190,6 +192,8 @@ struct ssp_device { int irq; int drcmr_rx; int drcmr_tx; + + struct device_node *of_node; }; /** @@ -218,11 +222,18 @@ static inline u32 pxa_ssp_read_reg(struct ssp_device *dev, u32 reg) #ifdef CONFIG_ARCH_PXA struct ssp_device *pxa_ssp_request(int port, const char *label); void pxa_ssp_free(struct ssp_device *); +struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node, + const char *label); #else static inline struct ssp_device *pxa_ssp_request(int port, const char *label) { return NULL; } +static inline struct ssp_device *pxa_ssp_request_of(const struct device_node *n, + const char *name) +{ + return NULL; +} static inline void pxa_ssp_free(struct ssp_device *ssp) {} #endif -- cgit v1.2.3-71-gd317 From e81c8f18afc4fdd6e34d8c83814b8b5134dbb30f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 6 Aug 2013 18:42:34 +0530 Subject: pinctrl: pinconf-generic: add generic APIs for mapping pinctrl node Add generic APIs to map the DT node and its sub node in pinconf generic driver. These APIs can be used from driver to parse the DT node who uses the pinconf generic APIs for defining their nodes. Changes from V1: - Add generic property for pins and functions in pinconf-generic. - Add APIs to map the DT and subnode. - Move common utils APIs to the pinctrl-utils from this file. - Update the binding document accordingly. Changes from V2: - Rebased the pinctrl binding doc on top of Stephen's cleanup. - Rename properties "pinctrl-pins" and "pinctrl-function" to "pins" and "function". Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Linus Walleij --- drivers/pinctrl/pinconf-generic.c | 96 +++++++++++++++++++++++++++++++++ include/linux/pinctrl/pinconf-generic.h | 6 +++ 2 files changed, 102 insertions(+) (limited to 'include') diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index 8594f033ac21..d9536caa9c41 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -24,6 +24,7 @@ #include #include "core.h" #include "pinconf.h" +#include "pinctrl-utils.h" #ifdef CONFIG_DEBUG_FS @@ -236,4 +237,99 @@ out: kfree(cfg); return ret; } + +int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, struct pinctrl_map **map, + unsigned *reserved_maps, unsigned *num_maps) +{ + int ret; + const char *function; + struct device *dev = pctldev->dev; + unsigned long *configs = NULL; + unsigned num_configs = 0; + unsigned reserve; + struct property *prop; + const char *group; + + ret = of_property_read_string(np, "function", &function); + if (ret < 0) { + /* EINVAL=missing, which is fine since it's optional */ + if (ret != -EINVAL) + dev_err(dev, + "could not parse property ti,function\n"); + function = NULL; + } + + ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs); + if (ret < 0) { + dev_err(dev, "could not parse node property\n"); + return ret; + } + + reserve = 0; + if (function != NULL) + reserve++; + if (num_configs) + reserve++; + ret = of_property_count_strings(np, "pins"); + if (ret < 0) { + dev_err(dev, "could not parse property ti,pins\n"); + goto exit; + } + reserve *= ret; + + ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, + num_maps, reserve); + if (ret < 0) + goto exit; + + of_property_for_each_string(np, "pins", prop, group) { + if (function) { + ret = pinctrl_utils_add_map_mux(pctldev, map, + reserved_maps, num_maps, group, + function); + if (ret < 0) + goto exit; + } + + if (num_configs) { + ret = pinctrl_utils_add_map_configs(pctldev, map, + reserved_maps, num_maps, group, configs, + num_configs, PIN_MAP_TYPE_CONFIGS_PIN); + if (ret < 0) + goto exit; + } + } + ret = 0; + +exit: + kfree(configs); + return ret; +} +EXPORT_SYMBOL_GPL(pinconf_generic_dt_subnode_to_map); + +int pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, struct pinctrl_map **map, + unsigned *num_maps) +{ + unsigned reserved_maps; + struct device_node *np; + int ret; + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + for_each_child_of_node(np_config, np) { + ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map, + &reserved_maps, num_maps); + if (ret < 0) { + pinctrl_utils_dt_free_map(pctldev, *map, *num_maps); + return ret; + } + } + return 0; +} +EXPORT_SYMBOL_GPL(pinconf_generic_dt_node_to_map); + #endif diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h index bf7e989abcb5..a472b93292a3 100644 --- a/include/linux/pinctrl/pinconf-generic.h +++ b/include/linux/pinctrl/pinconf-generic.h @@ -137,6 +137,12 @@ static inline unsigned long pinconf_to_config_packed(enum pin_config_param param return PIN_CONF_PACKED(param, argument); } +int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, struct pinctrl_map **map, + unsigned *reserved_maps, unsigned *num_maps); +int pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np_config, struct pinctrl_map **map, + unsigned *num_maps); #endif /* CONFIG_GENERIC_PINCONF */ #endif /* __LINUX_PINCTRL_PINCONF_GENERIC_H */ -- cgit v1.2.3-71-gd317 From 0a8d3e2412841c6b1dab1006fd5f7ab5b689db21 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 6 Aug 2013 18:42:35 +0530 Subject: pinctrl: palmas: add pincontrol driver TI Palmas series Power Management IC have multiple pins which can be configured for different functionality. This pins can be configured for different function. Also their properties like pull up/down, open drain enable/disable are configurable. Add support for pincontrol driver Palmas series device like TPS65913, TPS80036. The driver supports to be register from DT only. Changes from V1: - Add generic property for pins and functions in pinconf-generic. - Add APIs to map the DT and subnode. - Move common utils APIs to the pinctrl-utils from this file. - Update the binding document accordingly. Changes from V2: - Add ack by Lee. - Correct the binding docs. Signed-off-by: Laxman Dewangan Acked-by: Lee Jones Reviewed-by: Stephen Warren Signed-off-by: Linus Walleij --- .../devicetree/bindings/pinctrl/pinctrl-palmas.txt | 96 ++ drivers/pinctrl/Kconfig | 10 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-palmas.c | 1085 ++++++++++++++++++++ include/linux/mfd/palmas.h | 35 +- 5 files changed, 1216 insertions(+), 11 deletions(-) create mode 100644 Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt create mode 100644 drivers/pinctrl/pinctrl-palmas.c (limited to 'include') diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt new file mode 100644 index 000000000000..734d9b04d533 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-palmas.txt @@ -0,0 +1,96 @@ +Palmas Pincontrol bindings + +The pins of Palmas device can be set on different option and provides +the configuration for Pull UP/DOWN, open drain etc. + +Required properties: +- compatible: It must be one of following: + - "ti,palmas-pinctrl" for Palma series of the pincontrol. + - "ti,tps65913-pinctrl" for Palma series device TPS65913. + - "ti,tps80036-pinctrl" for Palma series device TPS80036. + +Please refer to pinctrl-bindings.txt in this directory for details of the +common pinctrl bindings used by client devices, including the meaning of the +phrase "pin configuration node". + +Palmas's pin configuration nodes act as a container for an arbitrary number of +subnodes. Each of these subnodes represents some desired configuration for a +list of pins. This configuration can include the mux function to select on +those pin(s), and various pin configuration parameters, such as pull-up, +open drain. + +The name of each subnode is not important; all subnodes should be enumerated +and processed purely based on their content. + +Each subnode only affects those parameters that are explicitly listed. In +other words, a subnode that lists a mux function but no pin configuration +parameters implies no information about any pin configuration parameters. +Similarly, a pin subnode that describes a pullup parameter implies no +information about e.g. the mux function. + +Optional properties: +- ti,palmas-enable-dvfs1: Enable DVFS1. Configure pins for DVFS1 mode. + Selection primary or secondary function associated to I2C2_SCL_SCE, + I2C2_SDA_SDO pin/pad for DVFS1 interface +- ti,palmas-enable-dvfs2: Enable DVFS2. Configure pins for DVFS2 mode. + Selection primary or secondary function associated to GPADC_START + and SYSEN2 pin/pad for DVFS2 interface + +This binding uses the following generic properties as defined in +pinctrl-bindings.txt: + +Required: pins +Options: function, bias-disable, bias-pull-up, bias-pull-down, + bias-pin-default, drive-open-drain. + +Note that many of these properties are only valid for certain specific pins. +See the Palmas device datasheet for complete details regarding which pins +support which functionality. + +Valid values for pin names are: + gpio0, gpio1, gpio2, gpio3, gpio4, gpio5, gpio6, gpio7, gpio8, gpio9, + gpio10, gpio11, gpio12, gpio13, gpio14, gpio15, vac, powergood, + nreswarm, pwrdown, gpadc_start, reset_in, nsleep, enable1, enable2, + int. + +Valid value of function names are: + gpio, led, pwm, regen, sysen, clk32kgaudio, id, vbus_det, chrg_det, + vac, vacok, powergood, usb_psel, msecure, pwrhold, int, nreswarm, + simrsto, simrsti, low_vbat, wireless_chrg1, rcm, pwrdown, gpadc_start, + reset_in, nsleep, enable. + +There are 4 special functions: opt0, opt1, opt2 and opt3. If any of these +functions is selected then directly pins register will be written with 0, 1, 2 +or 3 respectively if it is valid for that pins or list of pins. + +Example: + palmas: tps65913 { + .... + pinctrl { + compatible = "ti,tps65913-pinctrl"; + ti,palmas-enable-dvfs1; + pinctrl-names = "default"; + pinctrl-0 = <&palmas_pins_state>; + + palmas_pins_state: pinmux { + gpio0 { + pins = "gpio0"; + function = "id"; + bias-pull-up; + }; + + vac { + pins = "vac"; + function = "vacok"; + bias-pull-down; + }; + + gpio5 { + pins = "gpio5"; + function = "opt0"; + drive-open-drain = <1>; + }; + }; + }; + .... + }; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index bc830af1b23d..8542cd4a176a 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -261,6 +261,16 @@ config PINCTRL_EXYNOS5440 select PINMUX select PINCONF +config PINCTRL_PALMAS + bool "Pinctrl driver for the PALMA Series MFD devices" + depends on OF && MFD_PALMAS + select GENERIC_PINCONF + help + Palmas device supports the configuration of pins for different + functionality. This driver supports the pinmux, push-pull and + open drain configuration for the Palmas series devices like + TPS65913, TPS80036 etc. + config PINCTRL_S3C24XX bool "Samsung S3C24XX SoC pinctrl driver" depends on ARCH_S3C24XX diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index bc03a6eb8255..496d9bf9e1b9 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o obj-$(CONFIG_PINCTRL_STN8815) += pinctrl-nomadik-stn8815.o obj-$(CONFIG_PINCTRL_DB8500) += pinctrl-nomadik-db8500.o obj-$(CONFIG_PINCTRL_DB8540) += pinctrl-nomadik-db8540.o +obj-$(CONFIG_PINCTRL_PALMAS) += pinctrl-palmas.o obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_SIRF) += sirf/ diff --git a/drivers/pinctrl/pinctrl-palmas.c b/drivers/pinctrl/pinctrl-palmas.c new file mode 100644 index 000000000000..2697c2efd74b --- /dev/null +++ b/drivers/pinctrl/pinctrl-palmas.c @@ -0,0 +1,1085 @@ +/* + * pinctrl-palmas.c -- TI PALMAS series pin control driver. + * + * Copyright (c) 2013, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + * 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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; 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., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinconf.h" +#include "pinctrl-utils.h" + +#define PALMAS_PIN_GPIO0_ID 0 +#define PALMAS_PIN_GPIO1_VBUS_LED1_PWM1 1 +#define PALMAS_PIN_GPIO2_REGEN_LED2_PWM2 2 +#define PALMAS_PIN_GPIO3_CHRG_DET 3 +#define PALMAS_PIN_GPIO4_SYSEN1 4 +#define PALMAS_PIN_GPIO5_CLK32KGAUDIO_USB_PSEL 5 +#define PALMAS_PIN_GPIO6_SYSEN2 6 +#define PALMAS_PIN_GPIO7_MSECURE_PWRHOLD 7 +#define PALMAS_PIN_GPIO8_SIM1RSTI 8 +#define PALMAS_PIN_GPIO9_LOW_VBAT 9 +#define PALMAS_PIN_GPIO10_WIRELESS_CHRG1 10 +#define PALMAS_PIN_GPIO11_RCM 11 +#define PALMAS_PIN_GPIO12_SIM2RSTO 12 +#define PALMAS_PIN_GPIO13 13 +#define PALMAS_PIN_GPIO14 14 +#define PALMAS_PIN_GPIO15_SIM2RSTI 15 +#define PALMAS_PIN_VAC 16 +#define PALMAS_PIN_POWERGOOD_USB_PSEL 17 +#define PALMAS_PIN_NRESWARM 18 +#define PALMAS_PIN_PWRDOWN 19 +#define PALMAS_PIN_GPADC_START 20 +#define PALMAS_PIN_RESET_IN 21 +#define PALMAS_PIN_NSLEEP 22 +#define PALMAS_PIN_ENABLE1 23 +#define PALMAS_PIN_ENABLE2 24 +#define PALMAS_PIN_INT 25 +#define PALMAS_PIN_NUM (PALMAS_PIN_INT + 1) + +struct palmas_pin_function { + const char *name; + const char * const *groups; + unsigned ngroups; +}; + +struct palmas_pctrl_chip_info { + struct device *dev; + struct pinctrl_dev *pctl; + struct palmas *palmas; + int pins_current_opt[PALMAS_PIN_NUM]; + const struct palmas_pin_function *functions; + unsigned num_functions; + const struct palmas_pingroup *pin_groups; + int num_pin_groups; + const struct pinctrl_pin_desc *pins; + unsigned num_pins; +}; + +static const struct pinctrl_pin_desc palmas_pins_desc[] = { + PINCTRL_PIN(PALMAS_PIN_GPIO0_ID, "gpio0"), + PINCTRL_PIN(PALMAS_PIN_GPIO1_VBUS_LED1_PWM1, "gpio1"), + PINCTRL_PIN(PALMAS_PIN_GPIO2_REGEN_LED2_PWM2, "gpio2"), + PINCTRL_PIN(PALMAS_PIN_GPIO3_CHRG_DET, "gpio3"), + PINCTRL_PIN(PALMAS_PIN_GPIO4_SYSEN1, "gpio4"), + PINCTRL_PIN(PALMAS_PIN_GPIO5_CLK32KGAUDIO_USB_PSEL, "gpio5"), + PINCTRL_PIN(PALMAS_PIN_GPIO6_SYSEN2, "gpio6"), + PINCTRL_PIN(PALMAS_PIN_GPIO7_MSECURE_PWRHOLD, "gpio7"), + PINCTRL_PIN(PALMAS_PIN_GPIO8_SIM1RSTI, "gpio8"), + PINCTRL_PIN(PALMAS_PIN_GPIO9_LOW_VBAT, "gpio9"), + PINCTRL_PIN(PALMAS_PIN_GPIO10_WIRELESS_CHRG1, "gpio10"), + PINCTRL_PIN(PALMAS_PIN_GPIO11_RCM, "gpio11"), + PINCTRL_PIN(PALMAS_PIN_GPIO12_SIM2RSTO, "gpio12"), + PINCTRL_PIN(PALMAS_PIN_GPIO13, "gpio13"), + PINCTRL_PIN(PALMAS_PIN_GPIO14, "gpio14"), + PINCTRL_PIN(PALMAS_PIN_GPIO15_SIM2RSTI, "gpio15"), + PINCTRL_PIN(PALMAS_PIN_VAC, "vac"), + PINCTRL_PIN(PALMAS_PIN_POWERGOOD_USB_PSEL, "powergood"), + PINCTRL_PIN(PALMAS_PIN_NRESWARM, "nreswarm"), + PINCTRL_PIN(PALMAS_PIN_PWRDOWN, "pwrdown"), + PINCTRL_PIN(PALMAS_PIN_GPADC_START, "gpadc_start"), + PINCTRL_PIN(PALMAS_PIN_RESET_IN, "reset_in"), + PINCTRL_PIN(PALMAS_PIN_NSLEEP, "nsleep"), + PINCTRL_PIN(PALMAS_PIN_ENABLE1, "enable1"), + PINCTRL_PIN(PALMAS_PIN_ENABLE2, "enable2"), + PINCTRL_PIN(PALMAS_PIN_INT, "int"), +}; + +static const char * const opt0_groups[] = { + "gpio0", + "gpio1", + "gpio2", + "gpio3", + "gpio4", + "gpio5", + "gpio6", + "gpio7", + "gpio8", + "gpio9", + "gpio10", + "gpio11", + "gpio12", + "gpio13", + "gpio14", + "gpio15", + "vac", + "powergood", + "nreswarm", + "pwrdown", + "gpadc_start", + "reset_in", + "nsleep", + "enable1", + "enable2", + "int", +}; + +static const char * const opt1_groups[] = { + "gpio0", + "gpio1", + "gpio2", + "gpio3", + "gpio4", + "gpio5", + "gpio6", + "gpio7", + "gpio8", + "gpio9", + "gpio10", + "gpio11", + "gpio12", + "gpio15", + "vac", + "powergood", +}; + +static const char * const opt2_groups[] = { + "gpio1", + "gpio2", + "gpio5", + "gpio7", +}; + +static const char * const opt3_groups[] = { + "gpio1", + "gpio2", +}; + +static const char * const gpio_groups[] = { + "gpio0", + "gpio1", + "gpio2", + "gpio3", + "gpio4", + "gpio5", + "gpio6", + "gpio7", + "gpio8", + "gpio9", + "gpio10", + "gpio11", + "gpio12", + "gpio13", + "gpio14", + "gpio15", +}; + +static const char * const led_groups[] = { + "gpio1", + "gpio2", +}; + +static const char * const pwm_groups[] = { + "gpio1", + "gpio2", +}; + +static const char * const regen_groups[] = { + "gpio2", +}; + +static const char * const sysen_groups[] = { + "gpio4", + "gpio6", +}; + +static const char * const clk32kgaudio_groups[] = { + "gpio5", +}; + +static const char * const id_groups[] = { + "gpio0", +}; + +static const char * const vbus_det_groups[] = { + "gpio1", +}; + +static const char * const chrg_det_groups[] = { + "gpio3", +}; + +static const char * const vac_groups[] = { + "vac", +}; + +static const char * const vacok_groups[] = { + "vac", +}; + +static const char * const powergood_groups[] = { + "powergood", +}; + +static const char * const usb_psel_groups[] = { + "gpio5", + "powergood", +}; + +static const char * const msecure_groups[] = { + "gpio7", +}; + +static const char * const pwrhold_groups[] = { + "gpio7", +}; + +static const char * const int_groups[] = { + "int", +}; + +static const char * const nreswarm_groups[] = { + "nreswarm", +}; + +static const char * const simrsto_groups[] = { + "gpio12", +}; + +static const char * const simrsti_groups[] = { + "gpio8", + "gpio15", +}; + +static const char * const low_vbat_groups[] = { + "gpio9", +}; + +static const char * const wireless_chrg1_groups[] = { + "gpio10", +}; + +static const char * const rcm_groups[] = { + "gpio11", +}; + +static const char * const pwrdown_groups[] = { + "pwrdown", +}; + +static const char * const gpadc_start_groups[] = { + "gpadc_start", +}; + +static const char * const reset_in_groups[] = { + "reset_in", +}; + +static const char * const nsleep_groups[] = { + "nsleep", +}; + +static const char * const enable_groups[] = { + "enable1", + "enable2", +}; + +#define FUNCTION_GROUPS \ + FUNCTION_GROUP(opt0, OPTION0), \ + FUNCTION_GROUP(opt1, OPTION1), \ + FUNCTION_GROUP(opt2, OPTION2), \ + FUNCTION_GROUP(opt3, OPTION3), \ + FUNCTION_GROUP(gpio, GPIO), \ + FUNCTION_GROUP(led, LED), \ + FUNCTION_GROUP(pwm, PWM), \ + FUNCTION_GROUP(regen, REGEN), \ + FUNCTION_GROUP(sysen, SYSEN), \ + FUNCTION_GROUP(clk32kgaudio, CLK32KGAUDIO), \ + FUNCTION_GROUP(id, ID), \ + FUNCTION_GROUP(vbus_det, VBUS_DET), \ + FUNCTION_GROUP(chrg_det, CHRG_DET), \ + FUNCTION_GROUP(vac, VAC), \ + FUNCTION_GROUP(vacok, VACOK), \ + FUNCTION_GROUP(powergood, POWERGOOD), \ + FUNCTION_GROUP(usb_psel, USB_PSEL), \ + FUNCTION_GROUP(msecure, MSECURE), \ + FUNCTION_GROUP(pwrhold, PWRHOLD), \ + FUNCTION_GROUP(int, INT), \ + FUNCTION_GROUP(nreswarm, NRESWARM), \ + FUNCTION_GROUP(simrsto, SIMRSTO), \ + FUNCTION_GROUP(simrsti, SIMRSTI), \ + FUNCTION_GROUP(low_vbat, LOW_VBAT), \ + FUNCTION_GROUP(wireless_chrg1, WIRELESS_CHRG1), \ + FUNCTION_GROUP(rcm, RCM), \ + FUNCTION_GROUP(pwrdown, PWRDOWN), \ + FUNCTION_GROUP(gpadc_start, GPADC_START), \ + FUNCTION_GROUP(reset_in, RESET_IN), \ + FUNCTION_GROUP(nsleep, NSLEEP), \ + FUNCTION_GROUP(enable, ENABLE) + +static const struct palmas_pin_function palmas_pin_function[] = { +#undef FUNCTION_GROUP +#define FUNCTION_GROUP(fname, mux) \ + { \ + .name = #fname, \ + .groups = fname##_groups, \ + .ngroups = ARRAY_SIZE(fname##_groups), \ + } + + FUNCTION_GROUPS, +}; + +enum palmas_pinmux { +#undef FUNCTION_GROUP +#define FUNCTION_GROUP(fname, mux) PALMAS_PINMUX_##mux + FUNCTION_GROUPS, + PALMAS_PINMUX_NA = 0xFFFF, +}; + +struct palmas_pins_pullup_dn_info { + int pullup_dn_reg_base; + int pullup_dn_reg_add; + int pullup_dn_mask; + int normal_val; + int pull_up_val; + int pull_dn_val; +}; + +struct palmas_pins_od_info { + int od_reg_base; + int od_reg_add; + int od_mask; + int od_enable; + int od_disable; +}; + +struct palmas_pin_info { + enum palmas_pinmux mux_opt; + const struct palmas_pins_pullup_dn_info *pud_info; + const struct palmas_pins_od_info *od_info; +}; + +struct palmas_pingroup { + const char *name; + const unsigned pins[1]; + unsigned npins; + unsigned mux_reg_base; + unsigned mux_reg_add; + unsigned mux_reg_mask; + unsigned mux_bit_shift; + const struct palmas_pin_info *opt[4]; +}; + +#define PULL_UP_DN(_name, _rbase, _add, _mask, _nv, _uv, _dv) \ +static const struct palmas_pins_pullup_dn_info pud_##_name##_info = { \ + .pullup_dn_reg_base = PALMAS_##_rbase##_BASE, \ + .pullup_dn_reg_add = _add, \ + .pullup_dn_mask = _mask, \ + .normal_val = _nv, \ + .pull_up_val = _uv, \ + .pull_dn_val = _dv, \ +} + +PULL_UP_DN(nreswarm, PU_PD_OD, PALMAS_PU_PD_INPUT_CTRL1, 0x2, 0x0, 0x2, -1); +PULL_UP_DN(pwrdown, PU_PD_OD, PALMAS_PU_PD_INPUT_CTRL1, 0x4, 0x0, -1, 0x4); +PULL_UP_DN(gpadc_start, PU_PD_OD, PALMAS_PU_PD_INPUT_CTRL1, 0x30, 0x0, 0x20, 0x10); +PULL_UP_DN(reset_in, PU_PD_OD, PALMAS_PU_PD_INPUT_CTRL1, 0x40, 0x0, -1, 0x40); +PULL_UP_DN(nsleep, PU_PD_OD, PALMAS_PU_PD_INPUT_CTRL2, 0x3, 0x0, 0x2, 0x1); +PULL_UP_DN(enable1, PU_PD_OD, PALMAS_PU_PD_INPUT_CTRL2, 0xC, 0x0, 0x8, 0x4); +PULL_UP_DN(enable2, PU_PD_OD, PALMAS_PU_PD_INPUT_CTRL2, 0x30, 0x0, 0x20, 0x10); +PULL_UP_DN(vacok, PU_PD_OD, PALMAS_PU_PD_INPUT_CTRL3, 0x40, 0x0, -1, 0x40); +PULL_UP_DN(chrg_det, PU_PD_OD, PALMAS_PU_PD_INPUT_CTRL3, 0x10, 0x0, -1, 0x10); +PULL_UP_DN(pwrhold, PU_PD_OD, PALMAS_PU_PD_INPUT_CTRL3, 0x4, 0x0, -1, 0x4); +PULL_UP_DN(msecure, PU_PD_OD, PALMAS_PU_PD_INPUT_CTRL3, 0x1, 0x0, -1, 0x1); +PULL_UP_DN(id, USB_OTG, PALMAS_USB_ID_CTRL_SET, 0x40, 0x0, 0x40, -1); +PULL_UP_DN(gpio0, GPIO, PALMAS_PU_PD_GPIO_CTRL1, 0x04, 0, -1, 1); +PULL_UP_DN(gpio1, GPIO, PALMAS_PU_PD_GPIO_CTRL1, 0x0C, 0, 0x8, 0x4); +PULL_UP_DN(gpio2, GPIO, PALMAS_PU_PD_GPIO_CTRL1, 0x30, 0x0, 0x20, 0x10); +PULL_UP_DN(gpio3, GPIO, PALMAS_PU_PD_GPIO_CTRL1, 0x40, 0x0, -1, 0x40); +PULL_UP_DN(gpio4, GPIO, PALMAS_PU_PD_GPIO_CTRL2, 0x03, 0x0, 0x2, 0x1); +PULL_UP_DN(gpio5, GPIO, PALMAS_PU_PD_GPIO_CTRL2, 0x0c, 0x0, 0x8, 0x4); +PULL_UP_DN(gpio6, GPIO, PALMAS_PU_PD_GPIO_CTRL2, 0x30, 0x0, 0x20, 0x10); +PULL_UP_DN(gpio7, GPIO, PALMAS_PU_PD_GPIO_CTRL2, 0x40, 0x0, -1, 0x40); +PULL_UP_DN(gpio9, GPIO, PALMAS_PU_PD_GPIO_CTRL3, 0x0C, 0x0, 0x8, 0x4); +PULL_UP_DN(gpio10, GPIO, PALMAS_PU_PD_GPIO_CTRL3, 0x30, 0x0, 0x20, 0x10); +PULL_UP_DN(gpio11, GPIO, PALMAS_PU_PD_GPIO_CTRL3, 0xC0, 0x0, 0x80, 0x40); +PULL_UP_DN(gpio13, GPIO, PALMAS_PU_PD_GPIO_CTRL4, 0x04, 0x0, -1, 0x04); +PULL_UP_DN(gpio14, GPIO, PALMAS_PU_PD_GPIO_CTRL4, 0x30, 0x0, 0x20, 0x10); + +#define OD_INFO(_name, _rbase, _add, _mask, _ev, _dv) \ +static const struct palmas_pins_od_info od_##_name##_info = { \ + .od_reg_base = PALMAS_##_rbase##_BASE, \ + .od_reg_add = _add, \ + .od_mask = _mask, \ + .od_enable = _ev, \ + .od_disable = _dv, \ +} + +OD_INFO(gpio1, GPIO, PALMAS_OD_OUTPUT_GPIO_CTRL, 0x1, 0x1, 0x0); +OD_INFO(gpio2, GPIO, PALMAS_OD_OUTPUT_GPIO_CTRL, 0x2, 0x2, 0x0); +OD_INFO(gpio5, GPIO, PALMAS_OD_OUTPUT_GPIO_CTRL, 0x20, 0x20, 0x0); +OD_INFO(gpio10, GPIO, PALMAS_OD_OUTPUT_GPIO_CTRL2, 0x04, 0x04, 0x0); +OD_INFO(gpio13, GPIO, PALMAS_OD_OUTPUT_GPIO_CTRL2, 0x20, 0x20, 0x0); +OD_INFO(int, PU_PD_OD, PALMAS_OD_OUTPUT_CTRL, 0x8, 0x8, 0x0); +OD_INFO(pwm1, PU_PD_OD, PALMAS_OD_OUTPUT_CTRL, 0x20, 0x20, 0x0); +OD_INFO(pwm2, PU_PD_OD, PALMAS_OD_OUTPUT_CTRL, 0x80, 0x80, 0x0); +OD_INFO(vbus_det, PU_PD_OD, PALMAS_OD_OUTPUT_CTRL, 0x40, 0x40, 0x0); + +#define PIN_INFO(_name, _id, _pud_info, _od_info) \ +static const struct palmas_pin_info pin_##_name##_info = { \ + .mux_opt = PALMAS_PINMUX_##_id, \ + .pud_info = _pud_info, \ + .od_info = _od_info \ +} + +PIN_INFO(gpio0, GPIO, &pud_gpio0_info, NULL); +PIN_INFO(gpio1, GPIO, &pud_gpio1_info, &od_gpio1_info); +PIN_INFO(gpio2, GPIO, &pud_gpio2_info, &od_gpio2_info); +PIN_INFO(gpio3, GPIO, &pud_gpio3_info, NULL); +PIN_INFO(gpio4, GPIO, &pud_gpio4_info, NULL); +PIN_INFO(gpio5, GPIO, &pud_gpio5_info, &od_gpio5_info); +PIN_INFO(gpio6, GPIO, &pud_gpio6_info, NULL); +PIN_INFO(gpio7, GPIO, &pud_gpio7_info, NULL); +PIN_INFO(gpio8, GPIO, NULL, NULL); +PIN_INFO(gpio9, GPIO, &pud_gpio9_info, NULL); +PIN_INFO(gpio10, GPIO, &pud_gpio10_info, &od_gpio10_info); +PIN_INFO(gpio11, GPIO, &pud_gpio11_info, NULL); +PIN_INFO(gpio12, GPIO, NULL, NULL); +PIN_INFO(gpio13, GPIO, &pud_gpio13_info, &od_gpio13_info); +PIN_INFO(gpio14, GPIO, &pud_gpio14_info, NULL); +PIN_INFO(gpio15, GPIO, NULL, NULL); +PIN_INFO(id, ID, &pud_id_info, NULL); +PIN_INFO(led1, LED, NULL, NULL); +PIN_INFO(led2, LED, NULL, NULL); +PIN_INFO(regen, REGEN, NULL, NULL); +PIN_INFO(sysen1, SYSEN, NULL, NULL); +PIN_INFO(sysen2, SYSEN, NULL, NULL); +PIN_INFO(int, INT, NULL, &od_int_info); +PIN_INFO(pwm1, PWM, NULL, &od_pwm1_info); +PIN_INFO(pwm2, PWM, NULL, &od_pwm2_info); +PIN_INFO(vacok, VACOK, &pud_vacok_info, NULL); +PIN_INFO(chrg_det, CHRG_DET, &pud_chrg_det_info, NULL); +PIN_INFO(pwrhold, PWRHOLD, &pud_pwrhold_info, NULL); +PIN_INFO(msecure, MSECURE, &pud_msecure_info, NULL); +PIN_INFO(nreswarm, NA, &pud_nreswarm_info, NULL); +PIN_INFO(pwrdown, NA, &pud_pwrdown_info, NULL); +PIN_INFO(gpadc_start, NA, &pud_gpadc_start_info, NULL); +PIN_INFO(reset_in, NA, &pud_reset_in_info, NULL); +PIN_INFO(nsleep, NA, &pud_nsleep_info, NULL); +PIN_INFO(enable1, NA, &pud_enable1_info, NULL); +PIN_INFO(enable2, NA, &pud_enable2_info, NULL); +PIN_INFO(clk32kgaudio, CLK32KGAUDIO, NULL, NULL); +PIN_INFO(usb_psel, USB_PSEL, NULL, NULL); +PIN_INFO(vac, VAC, NULL, NULL); +PIN_INFO(powergood, POWERGOOD, NULL, NULL); +PIN_INFO(vbus_det, VBUS_DET, NULL, &od_vbus_det_info); +PIN_INFO(sim1rsti, SIMRSTI, NULL, NULL); +PIN_INFO(low_vbat, LOW_VBAT, NULL, NULL); +PIN_INFO(rcm, RCM, NULL, NULL); +PIN_INFO(sim2rsto, SIMRSTO, NULL, NULL); +PIN_INFO(sim2rsti, SIMRSTI, NULL, NULL); +PIN_INFO(wireless_chrg1, WIRELESS_CHRG1, NULL, NULL); + +#define PALMAS_PRIMARY_SECONDARY_NONE 0 +#define PALMAS_NONE_BASE 0 +#define PALMAS_PRIMARY_SECONDARY_INPUT3 PALMAS_PU_PD_INPUT_CTRL3 + +#define PALMAS_PINGROUP(pg_name, pin_id, base, reg, _mask, _bshift, o0, o1, o2, o3) \ + { \ + .name = #pg_name, \ + .pins = {PALMAS_PIN_##pin_id}, \ + .npins = 1, \ + .mux_reg_base = PALMAS_##base##_BASE, \ + .mux_reg_add = PALMAS_PRIMARY_SECONDARY_##reg, \ + .mux_reg_mask = _mask, \ + .mux_bit_shift = _bshift, \ + .opt = { \ + o0, \ + o1, \ + o2, \ + o3, \ + }, \ + } + +static const struct palmas_pingroup tps65913_pingroups[] = { + PALMAS_PINGROUP(gpio0, GPIO0_ID, PU_PD_OD, PAD1, 0x4, 0x2, &pin_gpio0_info, &pin_id_info, NULL, NULL), + PALMAS_PINGROUP(gpio1, GPIO1_VBUS_LED1_PWM1, PU_PD_OD, PAD1, 0x18, 0x3, &pin_gpio1_info, &pin_vbus_det_info, &pin_led1_info, &pin_pwm1_info), + PALMAS_PINGROUP(gpio2, GPIO2_REGEN_LED2_PWM2, PU_PD_OD, PAD1, 0x60, 0x5, &pin_gpio2_info, &pin_regen_info, &pin_led2_info, &pin_pwm2_info), + PALMAS_PINGROUP(gpio3, GPIO3_CHRG_DET, PU_PD_OD, PAD1, 0x80, 0x7, &pin_gpio3_info, &pin_chrg_det_info, NULL, NULL), + PALMAS_PINGROUP(gpio4, GPIO4_SYSEN1, PU_PD_OD, PAD1, 0x01, 0x0, &pin_gpio4_info, &pin_sysen1_info, NULL, NULL), + PALMAS_PINGROUP(gpio5, GPIO5_CLK32KGAUDIO_USB_PSEL, PU_PD_OD, PAD2, 0x6, 0x1, &pin_gpio5_info, &pin_clk32kgaudio_info, &pin_usb_psel_info, NULL), + PALMAS_PINGROUP(gpio6, GPIO6_SYSEN2, PU_PD_OD, PAD2, 0x08, 0x3, &pin_gpio6_info, &pin_sysen2_info, NULL, NULL), + PALMAS_PINGROUP(gpio7, GPIO7_MSECURE_PWRHOLD, PU_PD_OD, PAD2, 0x30, 0x4, &pin_gpio7_info, &pin_msecure_info, &pin_pwrhold_info, NULL), + PALMAS_PINGROUP(vac, VAC, PU_PD_OD, PAD1, 0x02, 0x1, &pin_vac_info, &pin_vacok_info, NULL, NULL), + PALMAS_PINGROUP(powergood, POWERGOOD_USB_PSEL, PU_PD_OD, PAD1, 0x01, 0x0, &pin_powergood_info, &pin_usb_psel_info, NULL, NULL), + PALMAS_PINGROUP(nreswarm, NRESWARM, NONE, NONE, 0x0, 0x0, &pin_nreswarm_info, NULL, NULL, NULL), + PALMAS_PINGROUP(pwrdown, PWRDOWN, NONE, NONE, 0x0, 0x0, &pin_pwrdown_info, NULL, NULL, NULL), + PALMAS_PINGROUP(gpadc_start, GPADC_START, NONE, NONE, 0x0, 0x0, &pin_gpadc_start_info, NULL, NULL, NULL), + PALMAS_PINGROUP(reset_in, RESET_IN, NONE, NONE, 0x0, 0x0, &pin_reset_in_info, NULL, NULL, NULL), + PALMAS_PINGROUP(nsleep, NSLEEP, NONE, NONE, 0x0, 0x0, &pin_nsleep_info, NULL, NULL, NULL), + PALMAS_PINGROUP(enable1, ENABLE1, NONE, NONE, 0x0, 0x0, &pin_enable1_info, NULL, NULL, NULL), + PALMAS_PINGROUP(enable2, ENABLE2, NONE, NONE, 0x0, 0x0, &pin_enable2_info, NULL, NULL, NULL), + PALMAS_PINGROUP(int, INT, NONE, NONE, 0x0, 0x0, &pin_int_info, NULL, NULL, NULL), +}; + +static const struct palmas_pingroup tps80036_pingroups[] = { + PALMAS_PINGROUP(gpio0, GPIO0_ID, PU_PD_OD, PAD1, 0x4, 0x2, &pin_gpio0_info, &pin_id_info, NULL, NULL), + PALMAS_PINGROUP(gpio1, GPIO1_VBUS_LED1_PWM1, PU_PD_OD, PAD1, 0x18, 0x3, &pin_gpio1_info, &pin_vbus_det_info, &pin_led1_info, &pin_pwm1_info), + PALMAS_PINGROUP(gpio2, GPIO2_REGEN_LED2_PWM2, PU_PD_OD, PAD1, 0x60, 0x5, &pin_gpio2_info, &pin_regen_info, &pin_led2_info, &pin_pwm2_info), + PALMAS_PINGROUP(gpio3, GPIO3_CHRG_DET, PU_PD_OD, PAD1, 0x80, 0x7, &pin_gpio3_info, &pin_chrg_det_info, NULL, NULL), + PALMAS_PINGROUP(gpio4, GPIO4_SYSEN1, PU_PD_OD, PAD1, 0x01, 0x0, &pin_gpio4_info, &pin_sysen1_info, NULL, NULL), + PALMAS_PINGROUP(gpio5, GPIO5_CLK32KGAUDIO_USB_PSEL, PU_PD_OD, PAD2, 0x6, 0x1, &pin_gpio5_info, &pin_clk32kgaudio_info, &pin_usb_psel_info, NULL), + PALMAS_PINGROUP(gpio6, GPIO6_SYSEN2, PU_PD_OD, PAD2, 0x08, 0x3, &pin_gpio6_info, &pin_sysen2_info, NULL, NULL), + PALMAS_PINGROUP(gpio7, GPIO7_MSECURE_PWRHOLD, PU_PD_OD, PAD2, 0x30, 0x4, &pin_gpio7_info, &pin_msecure_info, &pin_pwrhold_info, NULL), + PALMAS_PINGROUP(gpio8, GPIO8_SIM1RSTI, PU_PD_OD, PAD4, 0x01, 0x0, &pin_gpio8_info, &pin_sim1rsti_info, NULL, NULL), + PALMAS_PINGROUP(gpio9, GPIO9_LOW_VBAT, PU_PD_OD, PAD4, 0x02, 0x1, &pin_gpio9_info, &pin_low_vbat_info, NULL, NULL), + PALMAS_PINGROUP(gpio10, GPIO10_WIRELESS_CHRG1, PU_PD_OD, PAD4, 0x04, 0x2, &pin_gpio10_info, &pin_wireless_chrg1_info, NULL, NULL), + PALMAS_PINGROUP(gpio11, GPIO11_RCM, PU_PD_OD, PAD4, 0x08, 0x3, &pin_gpio11_info, &pin_rcm_info, NULL, NULL), + PALMAS_PINGROUP(gpio12, GPIO12_SIM2RSTO, PU_PD_OD, PAD4, 0x10, 0x4, &pin_gpio12_info, &pin_sim2rsto_info, NULL, NULL), + PALMAS_PINGROUP(gpio13, GPIO13, NONE, NONE, 0x00, 0x0, &pin_gpio13_info, NULL, NULL, NULL), + PALMAS_PINGROUP(gpio14, GPIO14, NONE, NONE, 0x00, 0x0, &pin_gpio14_info, NULL, NULL, NULL), + PALMAS_PINGROUP(gpio15, GPIO15_SIM2RSTI, PU_PD_OD, PAD4, 0x80, 0x7, &pin_gpio15_info, &pin_sim2rsti_info, NULL, NULL), + PALMAS_PINGROUP(vac, VAC, PU_PD_OD, PAD1, 0x02, 0x1, &pin_vac_info, &pin_vacok_info, NULL, NULL), + PALMAS_PINGROUP(powergood, POWERGOOD_USB_PSEL, PU_PD_OD, PAD1, 0x01, 0x0, &pin_powergood_info, &pin_usb_psel_info, NULL, NULL), + PALMAS_PINGROUP(nreswarm, NRESWARM, NONE, NONE, 0x0, 0x0, &pin_nreswarm_info, NULL, NULL, NULL), + PALMAS_PINGROUP(pwrdown, PWRDOWN, NONE, NONE, 0x0, 0x0, &pin_pwrdown_info, NULL, NULL, NULL), + PALMAS_PINGROUP(gpadc_start, GPADC_START, NONE, NONE, 0x0, 0x0, &pin_gpadc_start_info, NULL, NULL, NULL), + PALMAS_PINGROUP(reset_in, RESET_IN, NONE, NONE, 0x0, 0x0, &pin_reset_in_info, NULL, NULL, NULL), + PALMAS_PINGROUP(nsleep, NSLEEP, NONE, NONE, 0x0, 0x0, &pin_nsleep_info, NULL, NULL, NULL), + PALMAS_PINGROUP(enable1, ENABLE1, NONE, NONE, 0x0, 0x0, &pin_enable1_info, NULL, NULL, NULL), + PALMAS_PINGROUP(enable2, ENABLE2, NONE, NONE, 0x0, 0x0, &pin_enable2_info, NULL, NULL, NULL), + PALMAS_PINGROUP(int, INT, NONE, NONE, 0x0, 0x0, &pin_int_info, NULL, NULL, NULL), +}; + +static int palmas_pinctrl_get_pin_mux(struct palmas_pctrl_chip_info *pci) +{ + const struct palmas_pingroup *g; + unsigned int val; + int ret; + int i; + + for (i = 0; i < pci->num_pin_groups; ++i) { + g = &pci->pin_groups[i]; + if (g->mux_reg_base == PALMAS_NONE_BASE) { + pci->pins_current_opt[i] = 0; + continue; + } + ret = palmas_read(pci->palmas, g->mux_reg_base, + g->mux_reg_add, &val); + if (ret < 0) { + dev_err(pci->dev, "mux_reg 0x%02x read failed: %d\n", + g->mux_reg_add, ret); + return ret; + } + val &= g->mux_reg_mask; + pci->pins_current_opt[i] = val >> g->mux_bit_shift; + } + return 0; +} + +static int palmas_pinctrl_set_dvfs1(struct palmas_pctrl_chip_info *pci, + bool enable) +{ + int ret; + int val; + + val = enable ? PALMAS_PRIMARY_SECONDARY_PAD3_DVFS1 : 0; + ret = palmas_update_bits(pci->palmas, PALMAS_PU_PD_OD_BASE, + PALMAS_PRIMARY_SECONDARY_PAD3, + PALMAS_PRIMARY_SECONDARY_PAD3_DVFS1, val); + if (ret < 0) + dev_err(pci->dev, "SECONDARY_PAD3 update failed %d\n", ret); + return ret; +} + +static int palmas_pinctrl_set_dvfs2(struct palmas_pctrl_chip_info *pci, + bool enable) +{ + int ret; + int val; + + val = enable ? PALMAS_PRIMARY_SECONDARY_PAD3_DVFS2 : 0; + ret = palmas_update_bits(pci->palmas, PALMAS_PU_PD_OD_BASE, + PALMAS_PRIMARY_SECONDARY_PAD3, + PALMAS_PRIMARY_SECONDARY_PAD3_DVFS2, val); + if (ret < 0) + dev_err(pci->dev, "SECONDARY_PAD3 update failed %d\n", ret); + return ret; +} + +static int palmas_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct palmas_pctrl_chip_info *pci = pinctrl_dev_get_drvdata(pctldev); + + return pci->num_pin_groups; +} + +static const char *palmas_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned group) +{ + struct palmas_pctrl_chip_info *pci = pinctrl_dev_get_drvdata(pctldev); + + return pci->pin_groups[group].name; +} + +static int palmas_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned group, const unsigned **pins, unsigned *num_pins) +{ + struct palmas_pctrl_chip_info *pci = pinctrl_dev_get_drvdata(pctldev); + + *pins = pci->pin_groups[group].pins; + *num_pins = pci->pin_groups[group].npins; + return 0; +} + +static const struct pinctrl_ops palmas_pinctrl_ops = { + .get_groups_count = palmas_pinctrl_get_groups_count, + .get_group_name = palmas_pinctrl_get_group_name, + .get_group_pins = palmas_pinctrl_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map, + .dt_free_map = pinctrl_utils_dt_free_map, +}; + +static int palmas_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) +{ + struct palmas_pctrl_chip_info *pci = pinctrl_dev_get_drvdata(pctldev); + + return pci->num_functions; +} + +static const char *palmas_pinctrl_get_func_name(struct pinctrl_dev *pctldev, + unsigned function) +{ + struct palmas_pctrl_chip_info *pci = pinctrl_dev_get_drvdata(pctldev); + + return pci->functions[function].name; +} + +static int palmas_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, + unsigned function, const char * const **groups, + unsigned * const num_groups) +{ + struct palmas_pctrl_chip_info *pci = pinctrl_dev_get_drvdata(pctldev); + + *groups = pci->functions[function].groups; + *num_groups = pci->functions[function].ngroups; + return 0; +} + +static int palmas_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function, + unsigned group) +{ + struct palmas_pctrl_chip_info *pci = pinctrl_dev_get_drvdata(pctldev); + const struct palmas_pingroup *g; + int i; + int ret; + + g = &pci->pin_groups[group]; + + /* If direct option is provided here */ + if (function <= PALMAS_PINMUX_OPTION3) { + if (!g->opt[function]) { + dev_err(pci->dev, "Pin %s does not support option %d\n", + g->name, function); + return -EINVAL; + } + i = function; + } else { + for (i = 0; i < ARRAY_SIZE(g->opt); i++) { + if (!g->opt[i]) + continue; + if (g->opt[i]->mux_opt == function) + break; + } + if (WARN_ON(i == ARRAY_SIZE(g->opt))) { + dev_err(pci->dev, "Pin %s does not support option %d\n", + g->name, function); + return -EINVAL; + } + } + + if (g->mux_reg_base == PALMAS_NONE_BASE) { + if (WARN_ON(i != 0)) + return -EINVAL; + return 0; + } + + dev_dbg(pci->dev, "%s(): Base0x%02x:0x%02x:0x%02x:0x%02x\n", + __func__, g->mux_reg_base, g->mux_reg_add, + g->mux_reg_mask, i << g->mux_bit_shift); + + ret = palmas_update_bits(pci->palmas, g->mux_reg_base, g->mux_reg_add, + g->mux_reg_mask, i << g->mux_bit_shift); + if (ret < 0) { + dev_err(pci->dev, "Reg 0x%02x update failed: %d\n", + g->mux_reg_add, ret); + return ret; + } + pci->pins_current_opt[group] = i; + return 0; +} + +static const struct pinmux_ops palmas_pinmux_ops = { + .get_functions_count = palmas_pinctrl_get_funcs_count, + .get_function_name = palmas_pinctrl_get_func_name, + .get_function_groups = palmas_pinctrl_get_func_groups, + .enable = palmas_pinctrl_enable, +}; + +static int palmas_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin, unsigned long *config) +{ + struct palmas_pctrl_chip_info *pci = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(*config); + const struct palmas_pingroup *g; + const struct palmas_pin_info *opt; + unsigned int val; + int ret; + int base, add; + int rval; + int arg; + int group_nr; + + for (group_nr = 0; group_nr < pci->num_pin_groups; ++group_nr) { + if (pci->pin_groups[group_nr].pins[0] == pin) + break; + } + + if (group_nr == pci->num_pin_groups) { + dev_err(pci->dev, + "Pinconf is not supported for pin-id %d\n", pin); + return -ENOTSUPP; + } + + g = &pci->pin_groups[group_nr]; + opt = g->opt[pci->pins_current_opt[group_nr]]; + if (!opt) { + dev_err(pci->dev, + "Pinconf is not supported for pin %s\n", g->name); + return -ENOTSUPP; + } + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + if (!opt->pud_info) { + dev_err(pci->dev, + "PULL control not supported for pin %s\n", + g->name); + return -ENOTSUPP; + } + base = opt->pud_info->pullup_dn_reg_base; + add = opt->pud_info->pullup_dn_reg_add; + ret = palmas_read(pci->palmas, base, add, &val); + if (ret < 0) { + dev_err(pci->dev, "Reg 0x%02x read failed: %d\n", + add, ret); + return ret; + } + + rval = val & opt->pud_info->pullup_dn_mask; + arg = 0; + if ((opt->pud_info->normal_val >= 0) && + (opt->pud_info->normal_val == rval) && + (param == PIN_CONFIG_BIAS_DISABLE)) + arg = 1; + else if ((opt->pud_info->pull_up_val >= 0) && + (opt->pud_info->pull_up_val == rval) && + (param == PIN_CONFIG_BIAS_PULL_UP)) + arg = 1; + else if ((opt->pud_info->pull_dn_val >= 0) && + (opt->pud_info->pull_dn_val == rval) && + (param == PIN_CONFIG_BIAS_PULL_DOWN)) + arg = 1; + break; + + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + if (!opt->od_info) { + dev_err(pci->dev, + "OD control not supported for pin %s\n", + g->name); + return -ENOTSUPP; + } + base = opt->od_info->od_reg_base; + add = opt->od_info->od_reg_add; + ret = palmas_read(pci->palmas, base, add, &val); + if (ret < 0) { + dev_err(pci->dev, "Reg 0x%02x read failed: %d\n", + add, ret); + return ret; + } + rval = val & opt->od_info->od_mask; + arg = -1; + if ((opt->od_info->od_disable >= 0) && + (opt->od_info->od_disable == rval)) + arg = 0; + else if ((opt->od_info->od_enable >= 0) && + (opt->od_info->od_enable == rval)) + arg = 1; + if (arg < 0) { + dev_err(pci->dev, + "OD control not supported for pin %s\n", + g->name); + return -ENOTSUPP; + } + break; + + default: + dev_err(pci->dev, "Properties not supported\n"); + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, (u16)arg); + return 0; +} + +static int palmas_pinconf_set(struct pinctrl_dev *pctldev, + unsigned pin, unsigned long config) +{ + struct palmas_pctrl_chip_info *pci = pinctrl_dev_get_drvdata(pctldev); + enum pin_config_param param = pinconf_to_config_param(config); + u16 param_val = pinconf_to_config_argument(config); + const struct palmas_pingroup *g; + const struct palmas_pin_info *opt; + int ret; + int base, add, mask; + int rval; + int group_nr; + + for (group_nr = 0; group_nr < pci->num_pin_groups; ++group_nr) { + if (pci->pin_groups[group_nr].pins[0] == pin) + break; + } + + if (group_nr == pci->num_pin_groups) { + dev_err(pci->dev, + "Pinconf is not supported for pin-id %d\n", pin); + return -ENOTSUPP; + } + + g = &pci->pin_groups[group_nr]; + opt = g->opt[pci->pins_current_opt[group_nr]]; + if (!opt) { + dev_err(pci->dev, + "Pinconf is not supported for pin %s\n", g->name); + return -ENOTSUPP; + } + + switch (param) { + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + return 0; + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + if (!opt->pud_info) { + dev_err(pci->dev, + "PULL control not supported for pin %s\n", + g->name); + return -ENOTSUPP; + } + base = opt->pud_info->pullup_dn_reg_base; + add = opt->pud_info->pullup_dn_reg_add; + mask = opt->pud_info->pullup_dn_mask; + + if (param == PIN_CONFIG_BIAS_DISABLE) + rval = opt->pud_info->normal_val; + else if (param == PIN_CONFIG_BIAS_PULL_UP) + rval = opt->pud_info->pull_up_val; + else + rval = opt->pud_info->pull_dn_val; + + if (rval < 0) { + dev_err(pci->dev, + "PULL control not supported for pin %s\n", + g->name); + return -ENOTSUPP; + } + break; + + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + if (!opt->od_info) { + dev_err(pci->dev, + "OD control not supported for pin %s\n", + g->name); + return -ENOTSUPP; + } + base = opt->od_info->od_reg_base; + add = opt->od_info->od_reg_add; + mask = opt->od_info->od_mask; + if (param_val == 0) + rval = opt->od_info->od_disable; + else + rval = opt->od_info->od_enable; + if (rval < 0) { + dev_err(pci->dev, + "OD control not supported for pin %s\n", + g->name); + return -ENOTSUPP; + } + break; + default: + dev_err(pci->dev, "Properties not supported\n"); + return -ENOTSUPP; + } + + dev_dbg(pci->dev, "%s(): Add0x%02x:0x%02x:0x%02x:0x%02x\n", + __func__, base, add, mask, rval); + ret = palmas_update_bits(pci->palmas, base, add, mask, rval); + if (ret < 0) { + dev_err(pci->dev, "Reg 0x%02x update failed: %d\n", add, ret); + return ret; + } + return 0; +} + +static int palmas_pinconf_group_get(struct pinctrl_dev *pctldev, + unsigned group, unsigned long *config) +{ + dev_err(pctldev->dev, "palmas_pinconf_group_get op not supported\n"); + return -ENOTSUPP; +} + +static int palmas_pinconf_group_set(struct pinctrl_dev *pctldev, + unsigned group, unsigned long config) +{ + dev_err(pctldev->dev, "palmas_pinconf_group_set op not supported\n"); + return -ENOTSUPP; +} + +static const struct pinconf_ops palmas_pinconf_ops = { + .pin_config_get = palmas_pinconf_get, + .pin_config_set = palmas_pinconf_set, + .pin_config_group_get = palmas_pinconf_group_get, + .pin_config_group_set = palmas_pinconf_group_set, +}; + +static struct pinctrl_desc palmas_pinctrl_desc = { + .pctlops = &palmas_pinctrl_ops, + .pmxops = &palmas_pinmux_ops, + .confops = &palmas_pinconf_ops, + .owner = THIS_MODULE, +}; + +struct palmas_pinctrl_data { + const struct palmas_pingroup *pin_groups; + int num_pin_groups; +}; + +static struct palmas_pinctrl_data tps65913_pinctrl_data = { + .pin_groups = tps65913_pingroups, + .num_pin_groups = ARRAY_SIZE(tps65913_pingroups), +}; + +static struct palmas_pinctrl_data tps80036_pinctrl_data = { + .pin_groups = tps80036_pingroups, + .num_pin_groups = ARRAY_SIZE(tps80036_pingroups), +}; + +static struct of_device_id palmas_pinctrl_of_match[] = { + { .compatible = "ti,palmas-pinctrl", .data = &tps65913_pinctrl_data}, + { .compatible = "ti,tps65913-pinctrl", .data = &tps65913_pinctrl_data}, + { .compatible = "ti,tps80036-pinctrl", .data = &tps80036_pinctrl_data}, + { }, +}; +MODULE_DEVICE_TABLE(of, palmas_pinctrl_of_match); + +static int palmas_pinctrl_probe(struct platform_device *pdev) +{ + struct palmas_pctrl_chip_info *pci; + const struct palmas_pinctrl_data *pinctrl_data = &tps65913_pinctrl_data; + int ret; + bool enable_dvfs1 = false; + bool enable_dvfs2 = false; + + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_device(palmas_pinctrl_of_match, &pdev->dev); + pinctrl_data = match->data; + enable_dvfs1 = of_property_read_bool(pdev->dev.of_node, + "ti,palmas-enable-dvfs1"); + enable_dvfs2 = of_property_read_bool(pdev->dev.of_node, + "ti,palmas-enable-dvfs2"); + } + + pci = devm_kzalloc(&pdev->dev, sizeof(*pci), GFP_KERNEL); + if (!pci) { + dev_err(&pdev->dev, "Malloc for pci failed\n"); + return -ENOMEM; + } + + pci->dev = &pdev->dev; + pci->palmas = dev_get_drvdata(pdev->dev.parent); + + pci->pins = palmas_pins_desc; + pci->num_pins = ARRAY_SIZE(palmas_pins_desc); + pci->functions = palmas_pin_function; + pci->num_functions = ARRAY_SIZE(palmas_pin_function); + pci->pin_groups = pinctrl_data->pin_groups; + pci->num_pin_groups = pinctrl_data->num_pin_groups; + + platform_set_drvdata(pdev, pci); + + palmas_pinctrl_set_dvfs1(pci, enable_dvfs1); + palmas_pinctrl_set_dvfs2(pci, enable_dvfs2); + ret = palmas_pinctrl_get_pin_mux(pci); + if (ret < 0) { + dev_err(&pdev->dev, + "Reading pinctrol option register failed: %d\n", ret); + return ret; + } + + palmas_pinctrl_desc.name = dev_name(&pdev->dev); + palmas_pinctrl_desc.pins = palmas_pins_desc; + palmas_pinctrl_desc.npins = ARRAY_SIZE(palmas_pins_desc); + pci->pctl = pinctrl_register(&palmas_pinctrl_desc, &pdev->dev, pci); + if (!pci->pctl) { + dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); + return -ENODEV; + } + return 0; +} + +static int palmas_pinctrl_remove(struct platform_device *pdev) +{ + struct palmas_pctrl_chip_info *pci = platform_get_drvdata(pdev); + + pinctrl_unregister(pci->pctl); + return 0; +} + +static struct platform_driver palmas_pinctrl_driver = { + .driver = { + .name = "palmas-pinctrl", + .owner = THIS_MODULE, + .of_match_table = palmas_pinctrl_of_match, + }, + .probe = palmas_pinctrl_probe, + .remove = palmas_pinctrl_remove, +}; + +module_platform_driver(palmas_pinctrl_driver); + +MODULE_DESCRIPTION("Palmas pin control driver"); +MODULE_AUTHOR("Laxman Dewangan"); +MODULE_ALIAS("platform:palmas-pinctrl"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 1a8dd7afe084..2891f7cd902e 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -449,7 +449,7 @@ enum usb_irq_events { #define PALMAS_DVFS_BASE 0x180 #define PALMAS_PMU_CONTROL_BASE 0x1A0 #define PALMAS_RESOURCE_BASE 0x1D4 -#define PALMAS_PU_PD_OD_BASE 0x1F4 +#define PALMAS_PU_PD_OD_BASE 0x1F0 #define PALMAS_LED_BASE 0x200 #define PALMAS_INTERRUPT_BASE 0x210 #define PALMAS_USB_OTG_BASE 0x250 @@ -1734,16 +1734,20 @@ enum usb_irq_events { #define PALMAS_REGEN3_CTRL_MODE_ACTIVE_SHIFT 0 /* Registers for function PAD_CONTROL */ -#define PALMAS_PU_PD_INPUT_CTRL1 0x0 -#define PALMAS_PU_PD_INPUT_CTRL2 0x1 -#define PALMAS_PU_PD_INPUT_CTRL3 0x2 -#define PALMAS_OD_OUTPUT_CTRL 0x4 -#define PALMAS_POLARITY_CTRL 0x5 -#define PALMAS_PRIMARY_SECONDARY_PAD1 0x6 -#define PALMAS_PRIMARY_SECONDARY_PAD2 0x7 -#define PALMAS_I2C_SPI 0x8 -#define PALMAS_PU_PD_INPUT_CTRL4 0x9 -#define PALMAS_PRIMARY_SECONDARY_PAD3 0xA +#define PALMAS_OD_OUTPUT_CTRL2 0x2 +#define PALMAS_POLARITY_CTRL2 0x3 +#define PALMAS_PU_PD_INPUT_CTRL1 0x4 +#define PALMAS_PU_PD_INPUT_CTRL2 0x5 +#define PALMAS_PU_PD_INPUT_CTRL3 0x6 +#define PALMAS_PU_PD_INPUT_CTRL5 0x7 +#define PALMAS_OD_OUTPUT_CTRL 0x8 +#define PALMAS_POLARITY_CTRL 0x9 +#define PALMAS_PRIMARY_SECONDARY_PAD1 0xA +#define PALMAS_PRIMARY_SECONDARY_PAD2 0xB +#define PALMAS_I2C_SPI 0xC +#define PALMAS_PU_PD_INPUT_CTRL4 0xD +#define PALMAS_PRIMARY_SECONDARY_PAD3 0xE +#define PALMAS_PRIMARY_SECONDARY_PAD4 0xF /* Bit definitions for PU_PD_INPUT_CTRL1 */ #define PALMAS_PU_PD_INPUT_CTRL1_RESET_IN_PD 0x40 @@ -2501,6 +2505,15 @@ enum usb_irq_events { #define PALMAS_PU_PD_GPIO_CTRL1 0x6 #define PALMAS_PU_PD_GPIO_CTRL2 0x7 #define PALMAS_OD_OUTPUT_GPIO_CTRL 0x8 +#define PALMAS_GPIO_DATA_IN2 0x9 +#define PALMAS_GPIO_DATA_DIR2 0x0A +#define PALMAS_GPIO_DATA_OUT2 0x0B +#define PALMAS_GPIO_DEBOUNCE_EN2 0x0C +#define PALMAS_GPIO_CLEAR_DATA_OUT2 0x0D +#define PALMAS_GPIO_SET_DATA_OUT2 0x0E +#define PALMAS_PU_PD_GPIO_CTRL3 0x0F +#define PALMAS_PU_PD_GPIO_CTRL4 0x10 +#define PALMAS_OD_OUTPUT_GPIO_CTRL2 0x11 /* Bit definitions for GPIO_DATA_IN */ #define PALMAS_GPIO_DATA_IN_GPIO_7_IN 0x80 -- cgit v1.2.3-71-gd317 From 1542d9c35d8166c54e0616574954a0f48449f331 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:03 +0300 Subject: usb: chipidea: move vbus regulator operation to core The vbus regulator is a common element for USB vbus operation, So, move it from glue layer to core. Tested-by: Marek Vasut Signed-off-by: Michael Grzeschik Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 26 ++------------------------ drivers/usb/chipidea/core.c | 23 +++++++++++++++++++++++ include/linux/usb/chipidea.h | 1 + 3 files changed, 26 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 005ad5f11b69..30fdc2fd8771 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -19,7 +19,6 @@ #include #include #include -#include #include "ci.h" #include "ci_hdrc_imx.h" @@ -28,7 +27,6 @@ struct ci_hdrc_imx_data { struct usb_phy *phy; struct platform_device *ci_pdev; struct clk *clk; - struct regulator *reg_vbus; }; static const struct usbmisc_ops *usbmisc_ops; @@ -131,20 +129,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) goto err_clk; } - /* we only support host now, so enable vbus here */ - data->reg_vbus = devm_regulator_get(&pdev->dev, "vbus"); - if (!IS_ERR(data->reg_vbus)) { - ret = regulator_enable(data->reg_vbus); - if (ret) { - dev_err(&pdev->dev, - "Failed to enable vbus regulator, err=%d\n", - ret); - goto err_clk; - } - } else { - data->reg_vbus = NULL; - } - pdata.phy = data->phy; if (!pdev->dev.dma_mask) @@ -157,7 +141,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret); - goto err; + goto err_clk; } } @@ -169,7 +153,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Can't register ci_hdrc platform device, err=%d\n", ret); - goto err; + goto err_clk; } if (usbmisc_ops && usbmisc_ops->post) { @@ -190,9 +174,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) disable_device: ci_hdrc_remove_device(data->ci_pdev); -err: - if (data->reg_vbus) - regulator_disable(data->reg_vbus); err_clk: clk_disable_unprepare(data->clk); return ret; @@ -205,9 +186,6 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); ci_hdrc_remove_device(data->ci_pdev); - if (data->reg_vbus) - regulator_disable(data->reg_vbus); - if (data->phy) usb_phy_shutdown(data->phy); diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 5bb3e8154f87..13dbd511f59a 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -65,6 +65,7 @@ #include #include #include +#include #include "ci.h" #include "udc.h" @@ -342,6 +343,24 @@ static irqreturn_t ci_irq(int irq, void *data) return ret; } +static int ci_get_platdata(struct device *dev, + struct ci_hdrc_platform_data *platdata) +{ + /* Get the vbus regulator */ + platdata->reg_vbus = devm_regulator_get(dev, "vbus"); + if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) { + platdata->reg_vbus = NULL; /* no vbus regualator is needed */ + } else if (IS_ERR(platdata->reg_vbus)) { + dev_err(dev, "Getting regulator error: %ld\n", + PTR_ERR(platdata->reg_vbus)); + return PTR_ERR(platdata->reg_vbus); + } + + return 0; +} + static DEFINE_IDA(ci_ida); struct platform_device *ci_hdrc_add_device(struct device *dev, @@ -351,6 +370,10 @@ struct platform_device *ci_hdrc_add_device(struct device *dev, struct platform_device *pdev; int id, ret; + ret = ci_get_platdata(dev, platdata); + if (ret) + return ERR_PTR(ret); + id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL); if (id < 0) return ERR_PTR(id); diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index 25629948c842..ce4e1aa071b3 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -24,6 +24,7 @@ struct ci_hdrc_platform_data { #define CI_HDRC_CONTROLLER_RESET_EVENT 0 #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 void (*notify_event) (struct ci_hdrc *ci, unsigned event); + struct regulator *reg_vbus; }; /* Default offset of capability registers */ -- cgit v1.2.3-71-gd317 From 577b232fc9caba1b6f7a3bb9901c00b10e0ca1ba Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:08 +0300 Subject: usb: chipidea: add flag CI_HDRC_DUAL_ROLE_NOT_OTG Since we need otgsc to know vbus's status at some chipidea controllers even it is peripheral-only mode. Besides, some SoCs (eg, AR9331 SoC) don't have otgsc register even the DCCPARAMS_DC and DCCPARAMS_HC are both 1 at CAP_DCCPARAMS. We inroduce flag CI_HDRC_DUAL_ROLE_NOT_OTG to indicate if the controller is dual role, but not supports OTG. If this flag is not set, we follow the rule that if DCCPARAMS_DC and DCCPARAMS_HC are both 1 at CAP_DCCPARAMS, then this controller is otg capable. Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 37 ++++++++++++++++++++++++++++++------- include/linux/usb/chipidea.h | 5 +++++ 2 files changed, 35 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 75afc524b659..ab01e18bc2f8 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -426,6 +426,18 @@ static inline void ci_role_destroy(struct ci_hdrc *ci) ci_hdrc_host_destroy(ci); } +static void ci_get_otg_capable(struct ci_hdrc *ci) +{ + if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG) + ci->is_otg = false; + else + ci->is_otg = (hw_read(ci, CAP_DCCPARAMS, + DCCPARAMS_DC | DCCPARAMS_HC) + == (DCCPARAMS_DC | DCCPARAMS_HC)); + if (ci->is_otg) + dev_dbg(ci->dev, "It is OTG capable controller\n"); +} + static int ci_hdrc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -480,6 +492,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) return -ENODEV; } + ci_get_otg_capable(ci); + if (!ci->platdata->phy_mode) ci->platdata->phy_mode = of_usb_get_phy_mode(of_node); @@ -512,10 +526,22 @@ static int ci_hdrc_probe(struct platform_device *pdev) } if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { - ci->is_otg = true; - /* ID pin needs 1ms debouce time, we delay 2ms for safe */ - mdelay(2); - ci->role = ci_otg_role(ci); + if (ci->is_otg) { + /* + * ID pin needs 1ms debouce time, + * we delay 2ms for safe. + */ + mdelay(2); + ci->role = ci_otg_role(ci); + ci_hdrc_otg_init(ci); + } else { + /* + * If the controller is not OTG capable, but support + * role switch, the defalt role is gadget, and the + * user can switch it through debugfs. + */ + ci->role = CI_ROLE_GADGET; + } } else { ci->role = ci->roles[CI_ROLE_HOST] ? CI_ROLE_HOST @@ -534,9 +560,6 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (ret) goto stop; - if (ci->is_otg) - ci_hdrc_otg_init(ci); - ret = dbg_create_files(ci); if (!ret) return 0; diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index ce4e1aa071b3..10a607ce9090 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -20,6 +20,11 @@ struct ci_hdrc_platform_data { #define CI_HDRC_REQUIRE_TRANSCEIVER BIT(1) #define CI_HDRC_PULLUP_ON_VBUS BIT(2) #define CI_HDRC_DISABLE_STREAMING BIT(3) + /* + * Only set it when DCCPARAMS.DC==1 and DCCPARAMS.HC==1, + * but otg is not supported (no register otgsc). + */ +#define CI_HDRC_DUAL_ROLE_NOT_OTG BIT(4) enum usb_dr_mode dr_mode; #define CI_HDRC_CONTROLLER_RESET_EVENT 0 #define CI_HDRC_CONTROLLER_STOPPED_EVENT 1 -- cgit v1.2.3-71-gd317 From d268e9bc23f5df4a2f4167da246d277c2a468d3d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:14 +0300 Subject: usb: chipidea: retire flag CI_HDRC_PULLUP_ON_VBUS Currently, the controller only runs when the ci->vbus_active is true. So the flag CI_HDRC_PULLUP_ON_VBUS is useless no longer. If the user doesn't have otgsc, he/she needs to change ci_handle_vbus_change to update ci->vbus_active. Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 1 - drivers/usb/chipidea/ci_hdrc_msm.c | 1 - drivers/usb/chipidea/udc.c | 24 ++++++------------------ include/linux/usb/chipidea.h | 1 - 4 files changed, 6 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 30fdc2fd8771..cc53e63c9ce9 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -88,7 +88,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) .name = "ci_hdrc_imx", .capoffset = DEF_CAPOFFSET, .flags = CI_HDRC_REQUIRE_TRANSCEIVER | - CI_HDRC_PULLUP_ON_VBUS | CI_HDRC_DISABLE_STREAMING, }; int ret; diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index fb657ef50a9c..2d51d852b474 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -49,7 +49,6 @@ static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = { .name = "ci_hdrc_msm", .flags = CI_HDRC_REGS_SHARED | CI_HDRC_REQUIRE_TRANSCEIVER | - CI_HDRC_PULLUP_ON_VBUS | CI_HDRC_DISABLE_STREAMING, .notify_event = ci_hdrc_msm_notify_event, diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 45abf4d46a71..90c3572bd1b0 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1448,9 +1448,6 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) unsigned long flags; int gadget_ready = 0; - if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS)) - return -EOPNOTSUPP; - spin_lock_irqsave(&ci->lock, flags); ci->vbus_active = is_active; if (ci->driver) @@ -1635,13 +1632,11 @@ static int ci_udc_start(struct usb_gadget *gadget, ci->driver = driver; pm_runtime_get_sync(&ci->gadget.dev); - if (ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS) { - if (ci->vbus_active) { - hw_device_reset(ci, USBMODE_CM_DC); - } else { - pm_runtime_put_sync(&ci->gadget.dev); - goto done; - } + if (ci->vbus_active) { + hw_device_reset(ci, USBMODE_CM_DC); + } else { + pm_runtime_put_sync(&ci->gadget.dev); + goto done; } retval = hw_device_state(ci, ci->ep0out->qh.dma); @@ -1664,8 +1659,7 @@ static int ci_udc_stop(struct usb_gadget *gadget, spin_lock_irqsave(&ci->lock, flags); - if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS) || - ci->vbus_active) { + if (ci->vbus_active) { hw_device_state(ci, 0); if (ci->platdata->notify_event) ci->platdata->notify_event(ci, @@ -1800,12 +1794,6 @@ static int udc_start(struct ci_hdrc *ci) } } - if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS)) { - retval = hw_device_reset(ci, USBMODE_CM_DC); - if (retval) - goto put_transceiver; - } - if (ci->transceiver) { retval = otg_set_peripheral(ci->transceiver->otg, &ci->gadget); diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index 10a607ce9090..7d399671a566 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -18,7 +18,6 @@ struct ci_hdrc_platform_data { unsigned long flags; #define CI_HDRC_REGS_SHARED BIT(0) #define CI_HDRC_REQUIRE_TRANSCEIVER BIT(1) -#define CI_HDRC_PULLUP_ON_VBUS BIT(2) #define CI_HDRC_DISABLE_STREAMING BIT(3) /* * Only set it when DCCPARAMS.DC==1 and DCCPARAMS.HC==1, -- cgit v1.2.3-71-gd317 From 5c32b35b004f5ef70dcf62bbc42b8bed1e50b471 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 8 Aug 2013 14:09:31 -0600 Subject: PCI: Add hotplug_slot_ops.reset_slot() This optional callback allows hotplug controllers to perform slot specific resets. These may be necessary in cases where a normal secondary bus reset can interact with controller logic and expose spurious hotplugs. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- include/linux/pci_hotplug.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 8db71dcd6337..bd32109e607e 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -63,6 +63,9 @@ enum pcie_link_width { * @get_adapter_status: Called to get see if an adapter is present in the slot or not. * If this field is NULL, the value passed in the struct hotplug_slot_info * will be used when this value is requested by a user. + * @reset_slot: Optional interface to allow override of a bus reset for the + * slot for cases where a secondary bus reset can result in spurious + * hotplug events or where a slot can be reset independent of the bus. * * The table of function pointers that is passed to the hotplug pci core by a * hotplug pci driver. These functions are called by the hotplug pci core when @@ -80,6 +83,7 @@ struct hotplug_slot_ops { int (*get_attention_status) (struct hotplug_slot *slot, u8 *value); int (*get_latch_status) (struct hotplug_slot *slot, u8 *value); int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value); + int (*reset_slot) (struct hotplug_slot *slot, int probe); }; /** -- cgit v1.2.3-71-gd317 From 090a3c5322e900f468b3205b76d0837003ad57b2 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 8 Aug 2013 14:09:55 -0600 Subject: PCI: Add pci_reset_slot() and pci_reset_bus() Sometimes pci_reset_function() is not sufficient. We have cases where devices do not support any kind of reset, but there might be multiple functions on the bus preventing pci_reset_function() from doing a secondary bus reset. We also have cases where a device will advertise that it supports a PM reset, but really does nothing on D3hot->D0 (graphics cards are notorious for this). These devices often also have more than one function, so even blacklisting PM reset for them wouldn't allow a secondary bus reset through pci_reset_function(). If a driver supports multiple devices it should have the ability to induce a bus reset when it needs to. This patch provides that ability through pci_reset_slot() and pci_reset_bus(). It's the caller's responsibility when using these interfaces to understand that all of the devices in or below the slot (or on or below the bus) will be reset and therefore should be under control of the caller. PCI state of all the affected devices is saved and restored around these resets, but internal state of all of the affected devices is reset (which should be the intention). Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 2 + 2 files changed, 211 insertions(+) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d48d9febc241..12fccc24925c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3460,6 +3460,215 @@ int pci_reset_function(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_reset_function); +/* Lock devices from the top of the tree down */ +static void pci_bus_lock(struct pci_bus *bus) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + pci_dev_lock(dev); + if (dev->subordinate) + pci_bus_lock(dev->subordinate); + } +} + +/* Unlock devices from the bottom of the tree up */ +static void pci_bus_unlock(struct pci_bus *bus) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + if (dev->subordinate) + pci_bus_unlock(dev->subordinate); + pci_dev_unlock(dev); + } +} + +/* Lock devices from the top of the tree down */ +static void pci_slot_lock(struct pci_slot *slot) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &slot->bus->devices, bus_list) { + if (!dev->slot || dev->slot != slot) + continue; + pci_dev_lock(dev); + if (dev->subordinate) + pci_bus_lock(dev->subordinate); + } +} + +/* Unlock devices from the bottom of the tree up */ +static void pci_slot_unlock(struct pci_slot *slot) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &slot->bus->devices, bus_list) { + if (!dev->slot || dev->slot != slot) + continue; + if (dev->subordinate) + pci_bus_unlock(dev->subordinate); + pci_dev_unlock(dev); + } +} + +/* Save and disable devices from the top of the tree down */ +static void pci_bus_save_and_disable(struct pci_bus *bus) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + pci_dev_save_and_disable(dev); + if (dev->subordinate) + pci_bus_save_and_disable(dev->subordinate); + } +} + +/* + * Restore devices from top of the tree down - parent bridges need to be + * restored before we can get to subordinate devices. + */ +static void pci_bus_restore(struct pci_bus *bus) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + pci_dev_restore(dev); + if (dev->subordinate) + pci_bus_restore(dev->subordinate); + } +} + +/* Save and disable devices from the top of the tree down */ +static void pci_slot_save_and_disable(struct pci_slot *slot) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &slot->bus->devices, bus_list) { + if (!dev->slot || dev->slot != slot) + continue; + pci_dev_save_and_disable(dev); + if (dev->subordinate) + pci_bus_save_and_disable(dev->subordinate); + } +} + +/* + * Restore devices from top of the tree down - parent bridges need to be + * restored before we can get to subordinate devices. + */ +static void pci_slot_restore(struct pci_slot *slot) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &slot->bus->devices, bus_list) { + if (!dev->slot || dev->slot != slot) + continue; + pci_dev_restore(dev); + if (dev->subordinate) + pci_bus_restore(dev->subordinate); + } +} + +static int pci_slot_reset(struct pci_slot *slot, int probe) +{ + int rc; + + if (!slot) + return -ENOTTY; + + if (!probe) + pci_slot_lock(slot); + + might_sleep(); + + rc = pci_reset_hotplug_slot(slot->hotplug, probe); + + if (!probe) + pci_slot_unlock(slot); + + return rc; +} + +/** + * pci_reset_slot - reset a PCI slot + * @slot: PCI slot to reset + * + * A PCI bus may host multiple slots, each slot may support a reset mechanism + * independent of other slots. For instance, some slots may support slot power + * control. In the case of a 1:1 bus to slot architecture, this function may + * wrap the bus reset to avoid spurious slot related events such as hotplug. + * Generally a slot reset should be attempted before a bus reset. All of the + * function of the slot and any subordinate buses behind the slot are reset + * through this function. PCI config space of all devices in the slot and + * behind the slot is saved before and restored after reset. + * + * Return 0 on success, non-zero on error. + */ +int pci_reset_slot(struct pci_slot *slot) +{ + int rc; + + rc = pci_slot_reset(slot, 1); + if (rc) + return rc; + + pci_slot_save_and_disable(slot); + + rc = pci_slot_reset(slot, 0); + + pci_slot_restore(slot); + + return rc; +} +EXPORT_SYMBOL_GPL(pci_reset_slot); + +static int pci_bus_reset(struct pci_bus *bus, int probe) +{ + if (!bus->self) + return -ENOTTY; + + if (probe) + return 0; + + pci_bus_lock(bus); + + might_sleep(); + + pci_reset_bridge_secondary_bus(bus->self); + + pci_bus_unlock(bus); + + return 0; +} + +/** + * pci_reset_bus - reset a PCI bus + * @bus: top level PCI bus to reset + * + * Do a bus reset on the given bus and any subordinate buses, saving + * and restoring state of all devices. + * + * Return 0 on success, non-zero on error. + */ +int pci_reset_bus(struct pci_bus *bus) +{ + int rc; + + rc = pci_bus_reset(bus, 1); + if (rc) + return rc; + + pci_bus_save_and_disable(bus); + + rc = pci_bus_reset(bus, 0); + + pci_bus_restore(bus); + + return rc; +} +EXPORT_SYMBOL_GPL(pci_reset_bus); + /** * pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count * @dev: PCI device to query diff --git a/include/linux/pci.h b/include/linux/pci.h index 35c1bc4909f3..1a8fd3464daf 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -924,6 +924,8 @@ int pcie_set_mps(struct pci_dev *dev, int mps); int __pci_reset_function(struct pci_dev *dev); int __pci_reset_function_locked(struct pci_dev *dev); int pci_reset_function(struct pci_dev *dev); +int pci_reset_slot(struct pci_slot *slot); +int pci_reset_bus(struct pci_bus *bus); void pci_reset_bridge_secondary_bus(struct pci_dev *dev); void pci_update_resource(struct pci_dev *dev, int resno); int __must_check pci_assign_resource(struct pci_dev *dev, int i); -- cgit v1.2.3-71-gd317 From 6c742e714d8c282fd8f8b22d3e20b5141738c1ee Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Tue, 13 Aug 2013 17:51:11 +0200 Subject: ipip: add x-netns support This patch allows to switch the netns when packet is encapsulated or decapsulated. In other word, the encapsulated packet is received in a netns, where the lookup is done to find the tunnel. Once the tunnel is found, the packet is decapsulated and injecting into the corresponding interface which stands to another netns. When one of the two netns is removed, the tunnel is destroyed. Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/net/ip_tunnels.h | 2 +- net/ipv4/ip_gre.c | 4 ++-- net/ipv4/ip_tunnel.c | 43 ++++++++++++++++++++++++++++--------------- net/ipv4/ip_vti.c | 2 +- net/ipv4/ipip.c | 3 +-- 5 files changed, 33 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index c6acd9f8f877..5a76f2bef822 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -102,7 +102,7 @@ void ip_tunnel_dellink(struct net_device *dev, struct list_head *head); int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, struct rtnl_link_ops *ops, char *devname); -void ip_tunnel_delete_net(struct ip_tunnel_net *itn); +void ip_tunnel_delete_net(struct ip_tunnel_net *itn, struct rtnl_link_ops *ops); void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, const struct iphdr *tnl_params, const u8 protocol); diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 1f6eab66f7ce..bc3a76521deb 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -534,7 +534,7 @@ static int __net_init ipgre_init_net(struct net *net) static void __net_exit ipgre_exit_net(struct net *net) { struct ip_tunnel_net *itn = net_generic(net, ipgre_net_id); - ip_tunnel_delete_net(itn); + ip_tunnel_delete_net(itn, &ipgre_link_ops); } static struct pernet_operations ipgre_net_ops = { @@ -767,7 +767,7 @@ static int __net_init ipgre_tap_init_net(struct net *net) static void __net_exit ipgre_tap_exit_net(struct net *net) { struct ip_tunnel_net *itn = net_generic(net, gre_tap_net_id); - ip_tunnel_delete_net(itn); + ip_tunnel_delete_net(itn, &ipgre_tap_ops); } static struct pernet_operations ipgre_tap_net_ops = { diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index a351a003ee6b..a4d9126c7b51 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -350,7 +350,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev) struct flowi4 fl4; struct rtable *rt; - rt = ip_route_output_tunnel(dev_net(dev), &fl4, + rt = ip_route_output_tunnel(tunnel->net, &fl4, tunnel->parms.iph.protocol, iph->daddr, iph->saddr, tunnel->parms.o_key, @@ -365,7 +365,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev) } if (!tdev && tunnel->parms.link) - tdev = __dev_get_by_index(dev_net(dev), tunnel->parms.link); + tdev = __dev_get_by_index(tunnel->net, tunnel->parms.link); if (tdev) { hlen = tdev->hard_header_len + tdev->needed_headroom; @@ -654,7 +654,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, } } - err = iptunnel_xmit(dev_net(dev), rt, skb, + err = iptunnel_xmit(tunnel->net, rt, skb, fl4.saddr, fl4.daddr, protocol, ip_tunnel_ecn_encap(tos, inner_iph, skb), ttl, df); iptunnel_xmit_stats(err, &dev->stats, dev->tstats); @@ -821,11 +821,10 @@ static void ip_tunnel_dev_free(struct net_device *dev) void ip_tunnel_dellink(struct net_device *dev, struct list_head *head) { - struct net *net = dev_net(dev); struct ip_tunnel *tunnel = netdev_priv(dev); struct ip_tunnel_net *itn; - itn = net_generic(net, tunnel->ip_tnl_net_id); + itn = net_generic(tunnel->net, tunnel->ip_tnl_net_id); if (itn->fb_tunnel_dev != dev) { ip_tunnel_del(netdev_priv(dev)); @@ -855,6 +854,10 @@ int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, rtnl_lock(); itn->fb_tunnel_dev = __ip_tunnel_create(net, ops, &parms); + /* FB netdevice is special: we have one, and only one per netns. + * Allowing to move it to another netns is clearly unsafe. + */ + itn->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL; rtnl_unlock(); if (IS_ERR(itn->fb_tunnel_dev)) @@ -864,28 +867,39 @@ int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, } EXPORT_SYMBOL_GPL(ip_tunnel_init_net); -static void ip_tunnel_destroy(struct ip_tunnel_net *itn, struct list_head *head) +static void ip_tunnel_destroy(struct ip_tunnel_net *itn, struct list_head *head, + struct rtnl_link_ops *ops) { + struct net *net = dev_net(itn->fb_tunnel_dev); + struct net_device *dev, *aux; int h; + for_each_netdev_safe(net, dev, aux) + if (dev->rtnl_link_ops == ops) + unregister_netdevice_queue(dev, head); + for (h = 0; h < IP_TNL_HASH_SIZE; h++) { struct ip_tunnel *t; struct hlist_node *n; struct hlist_head *thead = &itn->tunnels[h]; hlist_for_each_entry_safe(t, n, thead, hash_node) - unregister_netdevice_queue(t->dev, head); + /* If dev is in the same netns, it has already + * been added to the list by the previous loop. + */ + if (!net_eq(dev_net(t->dev), net)) + unregister_netdevice_queue(t->dev, head); } if (itn->fb_tunnel_dev) unregister_netdevice_queue(itn->fb_tunnel_dev, head); } -void ip_tunnel_delete_net(struct ip_tunnel_net *itn) +void ip_tunnel_delete_net(struct ip_tunnel_net *itn, struct rtnl_link_ops *ops) { LIST_HEAD(list); rtnl_lock(); - ip_tunnel_destroy(itn, &list); + ip_tunnel_destroy(itn, &list, ops); unregister_netdevice_many(&list); rtnl_unlock(); } @@ -929,23 +943,21 @@ EXPORT_SYMBOL_GPL(ip_tunnel_newlink); int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[], struct ip_tunnel_parm *p) { - struct ip_tunnel *t, *nt; - struct net *net = dev_net(dev); + struct ip_tunnel *t; struct ip_tunnel *tunnel = netdev_priv(dev); + struct net *net = tunnel->net; struct ip_tunnel_net *itn = net_generic(net, tunnel->ip_tnl_net_id); if (dev == itn->fb_tunnel_dev) return -EINVAL; - nt = netdev_priv(dev); - t = ip_tunnel_find(itn, p, dev->type); if (t) { if (t->dev != dev) return -EEXIST; } else { - t = nt; + t = tunnel; if (dev->type != ARPHRD_ETHER) { unsigned int nflags = 0; @@ -984,6 +996,7 @@ int ip_tunnel_init(struct net_device *dev) } tunnel->dev = dev; + tunnel->net = dev_net(dev); strcpy(tunnel->parms.name, dev->name); iph->version = 4; iph->ihl = 5; @@ -994,8 +1007,8 @@ EXPORT_SYMBOL_GPL(ip_tunnel_init); void ip_tunnel_uninit(struct net_device *dev) { - struct net *net = dev_net(dev); struct ip_tunnel *tunnel = netdev_priv(dev); + struct net *net = tunnel->net; struct ip_tunnel_net *itn; itn = net_generic(net, tunnel->ip_tnl_net_id); diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 79b263da4168..e805e7b3030e 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -318,7 +318,7 @@ static int __net_init vti_init_net(struct net *net) static void __net_exit vti_exit_net(struct net *net) { struct ip_tunnel_net *itn = net_generic(net, vti_net_id); - ip_tunnel_delete_net(itn); + ip_tunnel_delete_net(itn, &vti_link_ops); } static struct pernet_operations vti_net_ops = { diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 51fc2a1dcdd3..87bd2952c733 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -286,7 +286,6 @@ static void ipip_tunnel_setup(struct net_device *dev) dev->flags = IFF_NOARP; dev->iflink = 0; dev->addr_len = 4; - dev->features |= NETIF_F_NETNS_LOCAL; dev->features |= NETIF_F_LLTX; dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; @@ -437,7 +436,7 @@ static int __net_init ipip_init_net(struct net *net) static void __net_exit ipip_exit_net(struct net *net) { struct ip_tunnel_net *itn = net_generic(net, ipip_net_id); - ip_tunnel_delete_net(itn); + ip_tunnel_delete_net(itn, &ipip_link_ops); } static struct pernet_operations ipip_net_ops = { -- cgit v1.2.3-71-gd317 From 0bd8762824e73a3cce7b7560a97463301764b616 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Tue, 13 Aug 2013 17:51:12 +0200 Subject: ip6tnl: add x-netns support This patch allows to switch the netns when packet is encapsulated or decapsulated. In other word, the encapsulated packet is received in a netns, where the lookup is done to find the tunnel. Once the tunnel is found, the packet is decapsulated and injecting into the corresponding interface which stands to another netns. When one of the two netns is removed, the tunnel is destroyed. Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/net/ip6_tunnel.h | 1 + net/ipv6/ip6_gre.c | 5 +++++ net/ipv6/ip6_tunnel.c | 41 +++++++++++++++++++++++++++++++---------- 3 files changed, 37 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index 4da5de10d1d4..2265b0bf97e5 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -36,6 +36,7 @@ struct __ip6_tnl_parm { struct ip6_tnl { struct ip6_tnl __rcu *next; /* next tunnel in list */ struct net_device *dev; /* virtual device associated with tunnel */ + struct net *net; /* netns for packet i/o */ struct __ip6_tnl_parm parms; /* tunnel configuration parameters */ struct flowi fl; /* flowi template for xmit */ struct dst_entry *dst_cache; /* cached dst */ diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index ecd60733e5e2..f2d0a42f8057 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -335,6 +335,7 @@ static struct ip6_tnl *ip6gre_tunnel_locate(struct net *net, dev->rtnl_link_ops = &ip6gre_link_ops; nt->dev = dev; + nt->net = dev_net(dev); ip6gre_tnl_link_config(nt, 1); if (register_netdevice(dev) < 0) @@ -1255,6 +1256,7 @@ static int ip6gre_tunnel_init(struct net_device *dev) tunnel = netdev_priv(dev); tunnel->dev = dev; + tunnel->net = dev_net(dev); strcpy(tunnel->parms.name, dev->name); memcpy(dev->dev_addr, &tunnel->parms.laddr, sizeof(struct in6_addr)); @@ -1275,6 +1277,7 @@ static void ip6gre_fb_tunnel_init(struct net_device *dev) struct ip6_tnl *tunnel = netdev_priv(dev); tunnel->dev = dev; + tunnel->net = dev_net(dev); strcpy(tunnel->parms.name, dev->name); tunnel->hlen = sizeof(struct ipv6hdr) + 4; @@ -1450,6 +1453,7 @@ static int ip6gre_tap_init(struct net_device *dev) tunnel = netdev_priv(dev); tunnel->dev = dev; + tunnel->net = dev_net(dev); strcpy(tunnel->parms.name, dev->name); ip6gre_tnl_link_config(tunnel, 1); @@ -1501,6 +1505,7 @@ static int ip6gre_newlink(struct net *src_net, struct net_device *dev, eth_hw_addr_random(dev); nt->dev = dev; + nt->net = dev_net(dev); ip6gre_tnl_link_config(nt, !tb[IFLA_MTU]); /* Can use a lockless transmit, unless we generate output sequences */ diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 1e55866cead7..cc3bb201b8b0 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -315,6 +315,7 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p) t = netdev_priv(dev); t->parms = *p; + t->net = dev_net(dev); err = ip6_tnl_create2(dev); if (err < 0) goto failed_free; @@ -374,7 +375,7 @@ static void ip6_tnl_dev_uninit(struct net_device *dev) { struct ip6_tnl *t = netdev_priv(dev); - struct net *net = dev_net(dev); + struct net *net = t->net; struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); if (dev == ip6n->fb_tnl_dev) @@ -741,7 +742,7 @@ int ip6_tnl_rcv_ctl(struct ip6_tnl *t, { struct __ip6_tnl_parm *p = &t->parms; int ret = 0; - struct net *net = dev_net(t->dev); + struct net *net = t->net; if ((p->flags & IP6_TNL_F_CAP_RCV) || ((p->flags & IP6_TNL_F_CAP_PER_PACKET) && @@ -827,6 +828,9 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol, tstats->rx_packets++; tstats->rx_bytes += skb->len; + if (!net_eq(t->net, dev_net(t->dev))) + skb_scrub_packet(skb); + netif_rx(skb); rcu_read_unlock(); @@ -895,7 +899,7 @@ int ip6_tnl_xmit_ctl(struct ip6_tnl *t) { struct __ip6_tnl_parm *p = &t->parms; int ret = 0; - struct net *net = dev_net(t->dev); + struct net *net = t->net; if (p->flags & IP6_TNL_F_CAP_XMIT) { struct net_device *ldev = NULL; @@ -945,8 +949,8 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, int encap_limit, __u32 *pmtu) { - struct net *net = dev_net(dev); struct ip6_tnl *t = netdev_priv(dev); + struct net *net = t->net; struct net_device_stats *stats = &t->dev->stats; struct ipv6hdr *ipv6h = ipv6_hdr(skb); struct ipv6_tel_txoption opt; @@ -996,6 +1000,9 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, goto tx_err_dst_release; } + if (!net_eq(t->net, dev_net(dev))) + skb_scrub_packet(skb); + /* * Okay, now see if we can stuff it in the buffer as-is. */ @@ -1202,7 +1209,7 @@ static void ip6_tnl_link_config(struct ip6_tnl *t) int strict = (ipv6_addr_type(&p->raddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL)); - struct rt6_info *rt = rt6_lookup(dev_net(dev), + struct rt6_info *rt = rt6_lookup(t->net, &p->raddr, &p->laddr, p->link, strict); @@ -1251,7 +1258,7 @@ ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p) static int ip6_tnl_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p) { - struct net *net = dev_net(t->dev); + struct net *net = t->net; struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); int err; @@ -1463,7 +1470,6 @@ static void ip6_tnl_dev_setup(struct net_device *dev) dev->mtu-=8; dev->flags |= IFF_NOARP; dev->addr_len = sizeof(struct in6_addr); - dev->features |= NETIF_F_NETNS_LOCAL; dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; } @@ -1479,6 +1485,7 @@ ip6_tnl_dev_init_gen(struct net_device *dev) struct ip6_tnl *t = netdev_priv(dev); t->dev = dev; + t->net = dev_net(dev); dev->tstats = alloc_percpu(struct pcpu_tstats); if (!dev->tstats) return -ENOMEM; @@ -1596,9 +1603,9 @@ static int ip6_tnl_newlink(struct net *src_net, struct net_device *dev, static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { - struct ip6_tnl *t; + struct ip6_tnl *t = netdev_priv(dev); struct __ip6_tnl_parm p; - struct net *net = dev_net(dev); + struct net *net = t->net; struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); if (dev == ip6n->fb_tnl_dev) @@ -1699,14 +1706,24 @@ static struct xfrm6_tunnel ip6ip6_handler __read_mostly = { static void __net_exit ip6_tnl_destroy_tunnels(struct ip6_tnl_net *ip6n) { + struct net *net = dev_net(ip6n->fb_tnl_dev); + struct net_device *dev, *aux; int h; struct ip6_tnl *t; LIST_HEAD(list); + for_each_netdev_safe(net, dev, aux) + if (dev->rtnl_link_ops == &ip6_link_ops) + unregister_netdevice_queue(dev, &list); + for (h = 0; h < HASH_SIZE; h++) { t = rtnl_dereference(ip6n->tnls_r_l[h]); while (t != NULL) { - unregister_netdevice_queue(t->dev, &list); + /* If dev is in the same netns, it has already + * been added to the list by the previous loop. + */ + if (!net_eq(dev_net(t->dev), net)) + unregister_netdevice_queue(t->dev, &list); t = rtnl_dereference(t->next); } } @@ -1732,6 +1749,10 @@ static int __net_init ip6_tnl_init_net(struct net *net) if (!ip6n->fb_tnl_dev) goto err_alloc_dev; dev_net_set(ip6n->fb_tnl_dev, net); + /* FB netdevice is special: we have one, and only one per netns. + * Allowing to move it to another netns is clearly unsafe. + */ + ip6n->fb_tnl_dev->features |= NETIF_F_NETNS_LOCAL; err = ip6_fb_tnl_dev_init(ip6n->fb_tnl_dev); if (err < 0) -- cgit v1.2.3-71-gd317 From d65a14587a9be853a887a1407db133df1fb68e29 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 12 Aug 2013 10:42:39 +0200 Subject: ASoC: pxa: use snd_dmaengine_dai_dma_data Use snd_dmaengine_dai_dma_data for passing the dma parameters from clients to the pxa pcm lib. This does no functional change, it's just an intermedia step to migrate the pxa bits over to dmaengine. The calculation of dcmd is a transition hack which will be removed again in a later patch. It's just there to make the transition more readable. Signed-off-by: Daniel Mack Acked-by: Mark Brown Signed-off-by: Mark Brown --- include/sound/pxa2xx-lib.h | 7 ----- sound/arm/pxa2xx-ac97.c | 26 ++++++++++-------- sound/arm/pxa2xx-pcm-lib.c | 52 +++++++++++++++++++++++++++++------ sound/arm/pxa2xx-pcm.c | 5 +++- sound/arm/pxa2xx-pcm.h | 6 ++-- sound/soc/pxa/mmp-pcm.c | 8 ++++-- sound/soc/pxa/mmp-sspa.c | 12 +++++--- sound/soc/pxa/pxa-ssp.c | 36 ++++++++---------------- sound/soc/pxa/pxa2xx-ac97.c | 67 +++++++++++++++++++++++---------------------- sound/soc/pxa/pxa2xx-i2s.c | 28 +++++++++---------- sound/soc/pxa/pxa2xx-pcm.c | 8 ++++-- 11 files changed, 142 insertions(+), 113 deletions(-) (limited to 'include') diff --git a/include/sound/pxa2xx-lib.h b/include/sound/pxa2xx-lib.h index 2fd3d251d9a5..56e818e4a1cb 100644 --- a/include/sound/pxa2xx-lib.h +++ b/include/sound/pxa2xx-lib.h @@ -6,13 +6,6 @@ /* PCM */ -struct pxa2xx_pcm_dma_params { - char *name; /* stream identifier */ - u32 dcmd; /* DMA descriptor dcmd field */ - volatile u32 *drcmr; /* the DMA request channel to use */ - u32 dev_addr; /* device physical address for DMA */ -}; - extern int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); extern int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream); diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index ce431e6e07cf..5066a3768b28 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -14,12 +14,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -41,20 +43,20 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_reset, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_out = { - .name = "AC97 PCM out", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(12), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_out_req = 12; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_in = { - .name = "AC97 PCM in", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(11), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_in_req = 11; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_in_req, }; static struct snd_pcm *pxa2xx_ac97_pcm; diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index 823359ed95e1..a61d7a9a995e 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -7,11 +7,13 @@ #include #include #include +#include #include #include #include #include +#include #include @@ -43,6 +45,35 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, size_t period = params_period_bytes(params); pxa_dma_desc *dma_desc; dma_addr_t dma_buff_phys, next_desc_phys; + u32 dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; + + /* temporary transition hack */ + switch (rtd->params->addr_width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + dcmd |= DCMD_WIDTH1; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + dcmd |= DCMD_WIDTH2; + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + dcmd |= DCMD_WIDTH4; + break; + default: + /* can't happen */ + break; + } + + switch (rtd->params->maxburst) { + case 8: + dcmd |= DCMD_BURST8; + break; + case 16: + dcmd |= DCMD_BURST16; + break; + case 32: + dcmd |= DCMD_BURST32; + break; + } snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = totsize; @@ -55,14 +86,14 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, dma_desc->ddadr = next_desc_phys; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dma_desc->dsadr = dma_buff_phys; - dma_desc->dtadr = rtd->params->dev_addr; + dma_desc->dtadr = rtd->params->addr; } else { - dma_desc->dsadr = rtd->params->dev_addr; + dma_desc->dsadr = rtd->params->addr; dma_desc->dtadr = dma_buff_phys; } if (period > totsize) period = totsize; - dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN; + dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN; dma_desc++; dma_buff_phys += period; } while (totsize -= period); @@ -76,8 +107,10 @@ int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) { struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; - if (rtd && rtd->params && rtd->params->drcmr) - *rtd->params->drcmr = 0; + if (rtd && rtd->params && rtd->params->filter_data) { + unsigned long req = *(unsigned long *) rtd->params->filter_data; + DRCMR(req) = 0; + } snd_pcm_set_runtime_buffer(substream, NULL); return 0; @@ -136,6 +169,7 @@ EXPORT_SYMBOL(pxa2xx_pcm_pointer); int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) { struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; + unsigned long req; if (!prtd || !prtd->params) return 0; @@ -146,7 +180,8 @@ int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) DCSR(prtd->dma_ch) &= ~DCSR_RUN; DCSR(prtd->dma_ch) = 0; DCMD(prtd->dma_ch) = 0; - *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD; + req = *(unsigned long *) prtd->params->filter_data; + DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD; return 0; } @@ -155,7 +190,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_prepare); void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) { struct snd_pcm_substream *substream = dev_id; - struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; int dcsr; dcsr = DCSR(dma_ch); @@ -164,8 +198,8 @@ void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) if (dcsr & DCSR_ENDINTR) { snd_pcm_period_elapsed(substream); } else { - printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", - rtd->params->name, dma_ch, dcsr); + printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n", + dma_ch, dcsr); snd_pcm_stream_lock(substream); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(substream); diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c index 26422a3584ea..69a2455b4472 100644 --- a/sound/arm/pxa2xx-pcm.c +++ b/sound/arm/pxa2xx-pcm.c @@ -11,8 +11,11 @@ */ #include +#include + #include #include +#include #include "pxa2xx-pcm.h" @@ -40,7 +43,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? client->playback_params : client->capture_params; - ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("dma", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) goto err2; diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h index 65f86b56ba42..2a8fc08d52a1 100644 --- a/sound/arm/pxa2xx-pcm.h +++ b/sound/arm/pxa2xx-pcm.h @@ -13,14 +13,14 @@ struct pxa2xx_runtime_data { int dma_ch; - struct pxa2xx_pcm_dma_params *params; + struct snd_dmaengine_dai_dma_data *params; pxa_dma_desc *dma_desc_array; dma_addr_t dma_desc_array_phys; }; struct pxa2xx_pcm_client { - struct pxa2xx_pcm_dma_params *playback_params; - struct pxa2xx_pcm_dma_params *capture_params; + struct snd_dmaengine_dai_dma_data *playback_params; + struct snd_dmaengine_dai_dma_data *capture_params; int (*startup)(struct snd_pcm_substream *); void (*shutdown)(struct snd_pcm_substream *); int (*prepare)(struct snd_pcm_substream *); diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c index 5d57e071cdf5..9a97843ab09f 100644 --- a/sound/soc/pxa/mmp-pcm.c +++ b/sound/soc/pxa/mmp-pcm.c @@ -17,6 +17,8 @@ #include #include #include +#include + #include #include #include @@ -67,7 +69,7 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream, { struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct pxa2xx_pcm_dma_params *dma_params; + struct snd_dmaengine_dai_dma_data *dma_params; struct dma_slave_config slave_config; int ret; @@ -80,10 +82,10 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream, return ret; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - slave_config.dst_addr = dma_params->dev_addr; + slave_config.dst_addr = dma_params->addr; slave_config.dst_maxburst = 4; } else { - slave_config.src_addr = dma_params->dev_addr; + slave_config.src_addr = dma_params->addr; slave_config.src_maxburst = 4; } diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c index 1605934d525e..41752a5fe3b0 100644 --- a/sound/soc/pxa/mmp-sspa.c +++ b/sound/soc/pxa/mmp-sspa.c @@ -27,12 +27,15 @@ #include #include #include +#include + #include #include #include #include #include #include +#include #include "mmp-sspa.h" /* @@ -40,7 +43,7 @@ */ struct sspa_priv { struct ssp_device *sspa; - struct pxa2xx_pcm_dma_params *dma_params; + struct snd_dmaengine_dai_dma_data *dma_params; struct clk *audio_clk; struct clk *sysclk; int dai_fmt; @@ -266,7 +269,7 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai); struct ssp_device *sspa = sspa_priv->sspa; - struct pxa2xx_pcm_dma_params *dma_params; + struct snd_dmaengine_dai_dma_data *dma_params; u32 sspa_ctrl; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -309,7 +312,7 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream, } dma_params = &sspa_priv->dma_params[substream->stream]; - dma_params->dev_addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + dma_params->addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? (sspa->phys_base + SSPA_TXD) : (sspa->phys_base + SSPA_RXD); snd_soc_dai_set_dma_data(cpu_dai, substream, dma_params); @@ -425,7 +428,8 @@ static int asoc_mmp_sspa_probe(struct platform_device *pdev) return -ENOMEM; priv->dma_params = devm_kzalloc(&pdev->dev, - 2 * sizeof(struct pxa2xx_pcm_dma_params), GFP_KERNEL); + 2 * sizeof(struct snd_dmaengine_dai_dma_data), + GFP_KERNEL); if (priv->dma_params == NULL) return -ENOMEM; diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 19296f22cb28..c0dcc3538e35 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -31,9 +32,9 @@ #include #include #include +#include #include -#include #include "../../arm/pxa2xx-pcm.h" #include "pxa-ssp.h" @@ -80,27 +81,14 @@ static void pxa_ssp_disable(struct ssp_device *ssp) __raw_writel(sscr0, ssp->mmio_base + SSCR0); } -struct pxa2xx_pcm_dma_data { - struct pxa2xx_pcm_dma_params params; - char name[20]; -}; - static void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4, - int out, struct pxa2xx_pcm_dma_params *dma_data) + int out, struct snd_dmaengine_dai_dma_data *dma) { - struct pxa2xx_pcm_dma_data *dma; - - dma = container_of(dma_data, struct pxa2xx_pcm_dma_data, params); - - snprintf(dma->name, 20, "SSP%d PCM %s %s", ssp->port_id, - width4 ? "32-bit" : "16-bit", out ? "out" : "in"); - - dma->params.name = dma->name; - dma->params.drcmr = &DRCMR(out ? ssp->drcmr_tx : ssp->drcmr_rx); - dma->params.dcmd = (out ? (DCMD_INCSRCADDR | DCMD_FLOWTRG) : - (DCMD_INCTRGADDR | DCMD_FLOWSRC)) | - (width4 ? DCMD_WIDTH4 : DCMD_WIDTH2) | DCMD_BURST16; - dma->params.dev_addr = ssp->phys_base + SSDR; + dma->filter_data = out ? &ssp->drcmr_tx : &ssp->drcmr_rx; + dma->addr_width = width4 ? DMA_SLAVE_BUSWIDTH_4_BYTES : + DMA_SLAVE_BUSWIDTH_2_BYTES; + dma->maxburst = 16; + dma->addr = ssp->phys_base + SSDR; } static int pxa_ssp_startup(struct snd_pcm_substream *substream, @@ -108,7 +96,7 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, { struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai); struct ssp_device *ssp = priv->ssp; - struct pxa2xx_pcm_dma_data *dma; + struct snd_dmaengine_dai_dma_data *dma; int ret = 0; if (!cpu_dai->active) { @@ -116,10 +104,10 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream, pxa_ssp_disable(ssp); } - dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL); + dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL); if (!dma) return -ENOMEM; - snd_soc_dai_set_dma_data(cpu_dai, substream, &dma->params); + snd_soc_dai_set_dma_data(cpu_dai, substream, dma); return ret; } @@ -560,7 +548,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream, u32 sspsp; int width = snd_pcm_format_physical_width(params_format(params)); int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf; - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 1475515712e6..f1059d999de6 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -14,15 +14,16 @@ #include #include #include +#include #include #include #include #include +#include #include #include -#include #include #include "pxa2xx-ac97.h" @@ -48,44 +49,44 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { .reset = pxa2xx_ac97_cold_reset, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = { - .name = "AC97 PCM Stereo out", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(12), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 12; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = { - .name = "AC97 PCM Stereo in", - .dev_addr = __PREG(PCDR), - .drcmr = &DRCMR(11), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 11; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_ac97_pcm_stereo_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = { - .name = "AC97 Aux PCM (Slot 5) Mono out", - .dev_addr = __PREG(MODR), - .drcmr = &DRCMR(10), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = { + .addr = __PREG(MODR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mono_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = { - .name = "AC97 Aux PCM (Slot 5) Mono in", - .dev_addr = __PREG(MODR), - .drcmr = &DRCMR(9), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = { + .addr = __PREG(MODR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mono_in_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = { - .name = "AC97 Mic PCM (Slot 6) Mono in", - .dev_addr = __PREG(MCDR), - .drcmr = &DRCMR(8), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST16 | DCMD_WIDTH2, +static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8; +static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = { + .addr = __PREG(MCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES, + .maxburst = 16, + .filter_data = &pxa2xx_ac97_pcm_aux_mic_mono_req, }; #ifdef CONFIG_PM @@ -119,7 +120,7 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dma_data = &pxa2xx_ac97_pcm_stereo_out; @@ -135,7 +136,7 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dma_data = &pxa2xx_ac97_pcm_aux_mono_out; diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index f7ca71664112..d5340a088858 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -23,9 +23,9 @@ #include #include #include +#include #include -#include #include #include "pxa2xx-i2s.h" @@ -82,20 +82,20 @@ static struct pxa_i2s_port pxa_i2s; static struct clk *clk_i2s; static int clk_ena = 0; -static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = { - .name = "I2S PCM Stereo out", - .dev_addr = __PREG(SADR), - .drcmr = &DRCMR(3), - .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_i2s_pcm_stereo_out_req = 3; +static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = { + .addr = __PREG(SADR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_i2s_pcm_stereo_out_req, }; -static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = { - .name = "I2S PCM Stereo in", - .dev_addr = __PREG(SADR), - .drcmr = &DRCMR(2), - .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | - DCMD_BURST32 | DCMD_WIDTH4, +static unsigned long pxa2xx_i2s_pcm_stereo_in_req = 2; +static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = { + .addr = __PREG(SADR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .maxburst = 32, + .filter_data = &pxa2xx_i2s_pcm_stereo_in_req, }; static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream, @@ -163,7 +163,7 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct pxa2xx_pcm_dma_params *dma_data; + struct snd_dmaengine_dai_dma_data *dma_data; BUG_ON(IS_ERR(clk_i2s)); clk_prepare_enable(clk_i2s); diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index ecff116cb7b0..0aa2d695064a 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -12,10 +12,12 @@ #include #include +#include #include #include #include +#include #include "../../arm/pxa2xx-pcm.h" @@ -25,7 +27,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; struct pxa2xx_runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct pxa2xx_pcm_dma_params *dma; + struct snd_dmaengine_dai_dma_data *dma; int ret; dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); @@ -39,7 +41,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, * with different params */ if (prtd->params == NULL) { prtd->params = dma; - ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("name", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) return ret; @@ -47,7 +49,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, } else if (prtd->params != dma) { pxa_free_dma(prtd->dma_ch); prtd->params = dma; - ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW, + ret = pxa_request_dma("name", DMA_PRIO_LOW, pxa2xx_pcm_dma_irq, substream); if (ret < 0) return ret; -- cgit v1.2.3-71-gd317 From 9a3d2b9beefd5b07c1d8f70ded01b88f203ee304 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 14 Aug 2013 14:06:05 -0600 Subject: PCI: Add pci_probe_reset_slot() and pci_probe_reset_bus() Users of pci_reset_bus() and pci_reset_slot() need a way to probe whether the bus or slot supports reset. Add trivial helper functions and export them as vfio-pci will make use of these. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 24 ++++++++++++++++++++++++ include/linux/pci.h | 2 ++ 2 files changed, 26 insertions(+) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ea5e70486174..7f89372483dd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3609,6 +3609,18 @@ static int pci_slot_reset(struct pci_slot *slot, int probe) return rc; } +/** + * pci_probe_reset_slot - probe whether a PCI slot can be reset + * @slot: PCI slot to probe + * + * Return 0 if slot can be reset, negative if a slot reset is not supported. + */ +int pci_probe_reset_slot(struct pci_slot *slot) +{ + return pci_slot_reset(slot, 1); +} +EXPORT_SYMBOL_GPL(pci_probe_reset_slot); + /** * pci_reset_slot - reset a PCI slot * @slot: PCI slot to reset @@ -3661,6 +3673,18 @@ static int pci_bus_reset(struct pci_bus *bus, int probe) return 0; } +/** + * pci_probe_reset_bus - probe whether a PCI bus can be reset + * @bus: PCI bus to probe + * + * Return 0 if bus can be reset, negative if a bus reset is not supported. + */ +int pci_probe_reset_bus(struct pci_bus *bus) +{ + return pci_bus_reset(bus, 1); +} +EXPORT_SYMBOL_GPL(pci_probe_reset_bus); + /** * pci_reset_bus - reset a PCI bus * @bus: top level PCI bus to reset diff --git a/include/linux/pci.h b/include/linux/pci.h index 1a8fd3464daf..daf40cd851df 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -924,7 +924,9 @@ int pcie_set_mps(struct pci_dev *dev, int mps); int __pci_reset_function(struct pci_dev *dev); int __pci_reset_function_locked(struct pci_dev *dev); int pci_reset_function(struct pci_dev *dev); +int pci_probe_reset_slot(struct pci_slot *slot); int pci_reset_slot(struct pci_slot *slot); +int pci_probe_reset_bus(struct pci_bus *bus); int pci_reset_bus(struct pci_bus *bus); void pci_reset_bridge_secondary_bus(struct pci_dev *dev); void pci_update_resource(struct pci_dev *dev, int resno); -- cgit v1.2.3-71-gd317 From b05e92545d9582be15699e4a33d0f93ac00b37dd Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Sat, 10 Aug 2013 12:27:26 +0200 Subject: brcmfmac: abstract tx packet processing functions Abstract brcmf_sdio_txpkt_prep and brcmf_sdio_txpkt_postp as a preparation of chained tx packets for host side tx glomming. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Arend van Spriel Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 16 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 237 +++++++++++++++------ .../net/wireless/brcm80211/brcmfmac/sdio_host.h | 2 +- include/linux/platform_data/brcmfmac-sdio.h | 6 + 4 files changed, 183 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index e3f3c48f86d4..e13b1a65c65f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -592,6 +592,7 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, u8 *buf, uint nbytes) { struct sk_buff *mypkt; + struct sk_buff_head pktq; int err; mypkt = brcmu_pkt_buf_get_skb(nbytes); @@ -602,7 +603,10 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, } memcpy(mypkt->data, buf, nbytes); - err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, mypkt); + __skb_queue_head_init(&pktq); + __skb_queue_tail(&pktq, mypkt); + err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, &pktq); + __skb_dequeue_tail(&pktq); brcmu_pkt_buf_free_skb(mypkt); return err; @@ -611,22 +615,18 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff *pkt) + uint flags, struct sk_buff_head *pktq) { uint width; int err = 0; - struct sk_buff_head pkt_list; brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", - fn, addr, pkt->len); + fn, addr, pktq->qlen); width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; brcmf_sdio_addrprep(sdiodev, width, &addr); - skb_queue_head_init(&pkt_list); - skb_queue_tail(&pkt_list, pkt); - err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, &pkt_list); - skb_dequeue_tail(&pkt_list); + err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, pktq); return err; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index db31312eba6a..aa4cacaf8b03 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -510,7 +510,6 @@ struct brcmf_sdio { #ifdef DEBUG static int qcount[NUMPRIO]; -static int tx_packets[NUMPRIO]; #endif /* DEBUG */ #define DEFAULT_SDIO_DRIVE_STRENGTH 6 /* in milliamps */ @@ -1759,85 +1758,185 @@ brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus) return; } +/* flag marking a dummy skb added for DMA alignment requirement */ +#define DUMMY_SKB_FLAG 0x10000 +/* bit mask of data length chopped from the previous packet */ +#define DUMMY_SKB_CHOP_LEN_MASK 0xffff +/** + * brcmf_sdio_txpkt_prep - packet preparation for transmit + * @bus: brcmf_sdio structure pointer + * @pktq: packet list pointer + * @chan: virtual channel to transmit the packet + * + * Processes to be applied to the packet + * - Align data buffer pointer + * - Align data buffer length + * - Prepare header + * Return: negative value if there is error + */ +static int +brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, + uint chan) +{ + u16 head_pad, tail_pad, tail_chop, pkt_len; + u16 head_align, sg_align; + u32 sw_header; + int ntail; + struct sk_buff *pkt_next, *pkt_new; + u8 *dat_buf; + unsigned blksize = bus->sdiodev->func[SDIO_FUNC_2]->cur_blksize; + + /* SDIO ADMA requires at least 32 bit alignment */ + head_align = 4; + sg_align = 4; + if (bus->sdiodev->pdata) { + head_align = bus->sdiodev->pdata->sd_head_align > 4 ? + bus->sdiodev->pdata->sd_head_align : 4; + sg_align = bus->sdiodev->pdata->sd_sgentry_align > 4 ? + bus->sdiodev->pdata->sd_sgentry_align : 4; + } + /* sg entry alignment should be a divisor of block size */ + WARN_ON(blksize % sg_align); + + pkt_next = pktq->next; + dat_buf = (u8 *)(pkt_next->data); + + /* Check head padding */ + head_pad = ((unsigned long)dat_buf % head_align); + if (head_pad) { + if (skb_headroom(pkt_next) < head_pad) { + bus->sdiodev->bus_if->tx_realloc++; + head_pad = 0; + if (skb_cow(pkt_next, head_pad)) + return -ENOMEM; + } + skb_push(pkt_next, head_pad); + dat_buf = (u8 *)(pkt_next->data); + memset(dat_buf, 0, head_pad + SDPCM_HDRLEN); + } + + /* Check tail padding */ + pkt_new = NULL; + tail_chop = pkt_next->len % sg_align; + tail_pad = sg_align - tail_chop; + tail_pad += blksize - (pkt_next->len + tail_pad) % blksize; + if (skb_tailroom(pkt_next) < tail_pad && pkt_next->len > blksize) { + pkt_new = brcmu_pkt_buf_get_skb(tail_pad + tail_chop); + if (pkt_new == NULL) + return -ENOMEM; + memcpy(pkt_new->data, + pkt_next->data + pkt_next->len - tail_chop, + tail_chop); + *(u32 *)(pkt_new->cb) = DUMMY_SKB_FLAG + tail_chop; + skb_trim(pkt_next, pkt_next->len - tail_chop); + __skb_queue_after(pktq, pkt_next, pkt_new); + } else { + ntail = pkt_next->data_len + tail_pad - + (pkt_next->end - pkt_next->tail); + if (skb_cloned(pkt_next) || ntail > 0) + if (pskb_expand_head(pkt_next, 0, ntail, GFP_ATOMIC)) + return -ENOMEM; + if (skb_linearize(pkt_next)) + return -ENOMEM; + dat_buf = (u8 *)(pkt_next->data); + __skb_put(pkt_next, tail_pad); + } + + /* Now prep the header */ + /* 4 bytes hardware header (frame tag) + * Byte 0~1: Frame length + * Byte 2~3: Checksum, bit-wise inverse of frame length + */ + if (pkt_new) + pkt_len = pkt_next->len + tail_chop; + else + pkt_len = pkt_next->len - tail_pad; + *(__le16 *)dat_buf = cpu_to_le16(pkt_len); + *(((__le16 *)dat_buf) + 1) = cpu_to_le16(~pkt_len); + /* 8 bytes software header + * Byte 0: Tx sequence number + * Byte 1: 4 MSB Channel number + * Byte 2: Reserved + * Byte 3: Data offset + * Byte 4~7: Reserved + */ + sw_header = bus->tx_seq; + sw_header |= ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK); + sw_header |= ((head_pad + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & + SDPCM_DOFFSET_MASK; + *(((__le32 *)dat_buf) + 1) = cpu_to_le32(sw_header); + *(((__le32 *)dat_buf) + 2) = 0; + + if (BRCMF_BYTES_ON() && + ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || + (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) + brcmf_dbg_hex_dump(true, pkt_next, pkt_len, "Tx Frame:\n"); + else if (BRCMF_HDRS_ON()) + brcmf_dbg_hex_dump(true, pkt_next, head_pad + SDPCM_HDRLEN, + "Tx Header:\n"); + + return 0; +} + +/** + * brcmf_sdio_txpkt_postp - packet post processing for transmit + * @bus: brcmf_sdio structure pointer + * @pktq: packet list pointer + * + * Processes to be applied to the packet + * - Remove head padding + * - Remove tail padding + */ +static void +brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) +{ + u8 *hdr; + u32 dat_offset; + u32 dummy_flags, chop_len; + struct sk_buff *pkt_next, *tmp, *pkt_prev; + + skb_queue_walk_safe(pktq, pkt_next, tmp) { + dummy_flags = *(u32 *)(pkt_next->cb); + if (dummy_flags & DUMMY_SKB_FLAG) { + chop_len = dummy_flags & DUMMY_SKB_CHOP_LEN_MASK; + if (chop_len) { + pkt_prev = pkt_next->prev; + memcpy(pkt_prev->data + pkt_prev->len, + pkt_next->data, chop_len); + skb_put(pkt_prev, chop_len); + } + __skb_unlink(pkt_next, pktq); + brcmu_pkt_buf_free_skb(pkt_next); + } else { + hdr = pkt_next->data + SDPCM_FRAMETAG_LEN; + dat_offset = le32_to_cpu(*(__le32 *)hdr); + dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >> + SDPCM_DOFFSET_SHIFT; + skb_pull(pkt_next, dat_offset); + } + } +} + /* Writes a HW/SW header into the packet and sends it. */ /* Assumes: (a) header space already there, (b) caller holds lock */ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, uint chan) { int ret; - u8 *frame; - u16 len, pad = 0; - u32 swheader; int i; + struct sk_buff_head localq; brcmf_dbg(TRACE, "Enter\n"); - frame = (u8 *) (pkt->data); - - /* Add alignment padding, allocate new packet if needed */ - pad = ((unsigned long)frame % BRCMF_SDALIGN); - if (pad) { - if (skb_headroom(pkt) < pad) { - brcmf_dbg(INFO, "insufficient headroom %d for %d pad\n", - skb_headroom(pkt), pad); - bus->sdiodev->bus_if->tx_realloc++; - ret = skb_cow(pkt, BRCMF_SDALIGN); - if (ret) - goto done; - pad = ((unsigned long)frame % BRCMF_SDALIGN); - } - skb_push(pkt, pad); - frame = (u8 *) (pkt->data); - memset(frame, 0, pad + SDPCM_HDRLEN); - } - /* precondition: pad < BRCMF_SDALIGN */ - - /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ - len = (u16) (pkt->len); - *(__le16 *) frame = cpu_to_le16(len); - *(((__le16 *) frame) + 1) = cpu_to_le16(~len); - - /* Software tag: channel, sequence number, data offset */ - swheader = - ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq | - (((pad + - SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); - - *(((__le32 *) frame) + 1) = cpu_to_le32(swheader); - *(((__le32 *) frame) + 2) = 0; - -#ifdef DEBUG - tx_packets[pkt->priority]++; -#endif - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && - ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || - (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)), - frame, len, "Tx Frame:\n"); - brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && - ((BRCMF_CTL_ON() && - chan == SDPCM_CONTROL_CHANNEL) || - (BRCMF_DATA_ON() && - chan != SDPCM_CONTROL_CHANNEL))) && - BRCMF_HDRS_ON(), - frame, min_t(u16, len, 16), "TxHdr:\n"); - - /* Raise len to next SDIO block to eliminate tail command */ - if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { - u16 pad = bus->blocksize - (len % bus->blocksize); - if ((pad <= bus->roundup) && (pad < bus->blocksize)) - len += pad; - } else if (len % BRCMF_SDALIGN) { - len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN); - } - - /* Some controllers have trouble with odd bytes -- round to even */ - if (len & (ALIGNMENT - 1)) - len = roundup(len, ALIGNMENT); + __skb_queue_head_init(&localq); + __skb_queue_tail(&localq, pkt); + ret = brcmf_sdio_txpkt_prep(bus, &localq, chan); + if (ret) + goto done; sdio_claim_host(bus->sdiodev->func[1]); ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, pkt); + SDIO_FUNC_2, F2SYNC, &localq); bus->sdcnt.f2txdata++; if (ret < 0) { @@ -1868,8 +1967,8 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; done: - /* restore pkt buffer pointer before calling tx complete routine */ - skb_pull(pkt, SDPCM_HDRLEN + pad); + brcmf_sdio_txpkt_postp(bus, &localq); + __skb_dequeue_tail(&localq); brcmf_txcomplete(bus->sdiodev->dev, pkt, ret == 0); return ret; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 09786a539950..2b5407f002e5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -208,7 +208,7 @@ extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, */ extern int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff *pkt); + uint flags, struct sk_buff_head *pktq); extern int brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, u8 *buf, uint nbytes); diff --git a/include/linux/platform_data/brcmfmac-sdio.h b/include/linux/platform_data/brcmfmac-sdio.h index b7174998c24a..e75dcbf2b230 100644 --- a/include/linux/platform_data/brcmfmac-sdio.h +++ b/include/linux/platform_data/brcmfmac-sdio.h @@ -94,6 +94,10 @@ void __init brcmfmac_init_pdata(void) * Set this to true if the SDIO host controller has higher align requirement * than 32 bytes for each scatterlist item. * + * sd_head_align: alignment requirement for start of data buffer + * + * sd_sgentry_align: length alignment requirement for each sg entry + * * power_on: This function is called by the brcmfmac when the module gets * loaded. This can be particularly useful for low power devices. The platform * spcific routine may for example decide to power up the complete device. @@ -121,6 +125,8 @@ struct brcmfmac_sdio_platform_data { unsigned int oob_irq_nr; unsigned long oob_irq_flags; bool broken_sg_support; + unsigned short sd_head_align; + unsigned short sd_sgentry_align; void (*power_on)(void); void (*power_off)(void); void (*reset)(void); -- cgit v1.2.3-71-gd317 From 0d74d4a161c9f9870039af414b712552c0ed6dfb Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 15 Aug 2013 21:38:49 +0200 Subject: pinctrl: add includes and ifdefs for non-DT builds Commit e81c8f18afc4fdd6e34d8c83814b8b5134dbb30f "pinctrl: pinconf-generic: add generic APIs for mapping pinctrl node" Added function prototypes with implicit dependencies on other header files causing build warnings like this: In file included from arch/arm/mach-ux500/board-mop500-pins.c:12:0: include/linux/pinctrl/pinconf-generic.h:142:3: warning: 'struct device_node' declared inside parameter list [enabled by default] unsigned *reserved_maps, unsigned *num_maps); ^ include/linux/pinctrl/pinconf-generic.h:142:3: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default] include/linux/pinctrl/pinconf-generic.h:142:3: warning: 'struct pinctrl_dev' declared inside parameter list [enabled by default] include/linux/pinctrl/pinconf-generic.h:145:3: warning: 'struct device_node' declared inside parameter list [enabled by default] unsigned *num_maps); ^ Let's just add ifdefs for non-DT systems (the actual code is already ifdefed) and #include to get the most important structs and forward-declare the pinctrl core structs. Reported-by: Olof Johansson Cc: Laxman Dewangan Signed-off-by: Linus Walleij --- include/linux/pinctrl/pinconf-generic.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h index a472b93292a3..83f5179ea0c5 100644 --- a/include/linux/pinctrl/pinconf-generic.h +++ b/include/linux/pinctrl/pinconf-generic.h @@ -137,12 +137,21 @@ static inline unsigned long pinconf_to_config_packed(enum pin_config_param param return PIN_CONF_PACKED(param, argument); } +#ifdef CONFIG_OF + +#include +struct pinctrl_dev; +struct pinctrl_map; + int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **map, unsigned *reserved_maps, unsigned *num_maps); int pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, unsigned *num_maps); + +#endif + #endif /* CONFIG_GENERIC_PINCONF */ #endif /* __LINUX_PINCTRL_PINCONF_GENERIC_H */ -- cgit v1.2.3-71-gd317 From 98d4f93c79b002f85480320fe63fefaa31d58b6c Mon Sep 17 00:00:00 2001 From: Jens Frederich Date: Thu, 15 Aug 2013 21:34:55 +0200 Subject: Staging: olpc_dcon: replace some magic numbers This patch replace some magic numbers. I believe it makes the driver more readable. The magic number 0x26 is the XO system embedded controller (EC) command 'DCON power enable/disable'. Number 0x41, and 0x42 are special memory controller settings register. The 0x41 initialize bit sequence 0x101 means: enable memory power down function and special SDRAM clock delay for synchronize SDRAM output and clock signal. The 0x42 initialize squence 0x101 is wrong. According to the specification Bit 8 is reserved, thus not in use. I removed it. Signed-off-by: Jens Frederich Signed-off-by: Greg Kroah-Hartman --- drivers/staging/olpc_dcon/olpc_dcon.c | 13 +++++++------ drivers/staging/olpc_dcon/olpc_dcon.h | 27 ++++++++++++++++++--------- include/linux/olpc-ec.h | 1 + 3 files changed, 26 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/staging/olpc_dcon/olpc_dcon.c b/drivers/staging/olpc_dcon/olpc_dcon.c index 7c460f2c0d23..5ca4fa4fd246 100644 --- a/drivers/staging/olpc_dcon/olpc_dcon.c +++ b/drivers/staging/olpc_dcon/olpc_dcon.c @@ -90,9 +90,10 @@ static int dcon_hw_init(struct dcon_priv *dcon, int is_init) /* SDRAM setup/hold time */ dcon_write(dcon, 0x3a, 0xc040); - dcon_write(dcon, 0x41, 0x0000); - dcon_write(dcon, 0x41, 0x0101); - dcon_write(dcon, 0x42, 0x0101); + dcon_write(dcon, DCON_REG_MEM_OPT_A, 0x0000); /* clear option bits */ + dcon_write(dcon, DCON_REG_MEM_OPT_A, + MEM_DLL_CLOCK_DELAY | MEM_POWER_DOWN); + dcon_write(dcon, DCON_REG_MEM_OPT_B, MEM_SOFT_RESET); /* Colour swizzle, AA, no passthrough, backlight */ if (is_init) { @@ -126,7 +127,7 @@ static int dcon_bus_stabilize(struct dcon_priv *dcon, int is_powered_down) power_up: if (is_powered_down) { x = 1; - x = olpc_ec_cmd(0x26, (unsigned char *)&x, 1, NULL, 0); + x = olpc_ec_cmd(EC_DCON_POWER_MODE, (u8 *)&x, 1, NULL, 0); if (x) { pr_warn("unable to force dcon to power up: %d!\n", x); return x; @@ -144,7 +145,7 @@ power_up: pr_err("unable to stabilize dcon's smbus, reasserting power and praying.\n"); BUG_ON(olpc_board_at_least(olpc_board(0xc2))); x = 0; - olpc_ec_cmd(0x26, (unsigned char *)&x, 1, NULL, 0); + olpc_ec_cmd(EC_DCON_POWER_MODE, (u8 *)&x, 1, NULL, 0); msleep(100); is_powered_down = 1; goto power_up; /* argh, stupid hardware.. */ @@ -208,7 +209,7 @@ static void dcon_sleep(struct dcon_priv *dcon, bool sleep) if (sleep) { x = 0; - x = olpc_ec_cmd(0x26, (unsigned char *)&x, 1, NULL, 0); + x = olpc_ec_cmd(EC_DCON_POWER_MODE, (u8 *)&x, 1, NULL, 0); if (x) pr_warn("unable to force dcon to power down: %d!\n", x); else diff --git a/drivers/staging/olpc_dcon/olpc_dcon.h b/drivers/staging/olpc_dcon/olpc_dcon.h index 997bded2949f..524ee49fbacb 100644 --- a/drivers/staging/olpc_dcon/olpc_dcon.h +++ b/drivers/staging/olpc_dcon/olpc_dcon.h @@ -22,15 +22,24 @@ #define MODE_DEBUG (1<<14) #define MODE_SELFTEST (1<<15) -#define DCON_REG_HRES 2 -#define DCON_REG_HTOTAL 3 -#define DCON_REG_HSYNC_WIDTH 4 -#define DCON_REG_VRES 5 -#define DCON_REG_VTOTAL 6 -#define DCON_REG_VSYNC_WIDTH 7 -#define DCON_REG_TIMEOUT 8 -#define DCON_REG_SCAN_INT 9 -#define DCON_REG_BRIGHT 10 +#define DCON_REG_HRES 0x2 +#define DCON_REG_HTOTAL 0x3 +#define DCON_REG_HSYNC_WIDTH 0x4 +#define DCON_REG_VRES 0x5 +#define DCON_REG_VTOTAL 0x6 +#define DCON_REG_VSYNC_WIDTH 0x7 +#define DCON_REG_TIMEOUT 0x8 +#define DCON_REG_SCAN_INT 0x9 +#define DCON_REG_BRIGHT 0x10 +#define DCON_REG_MEM_OPT_A 0x41 +#define DCON_REG_MEM_OPT_B 0x42 + +/* Load Delay Locked Loop (DLL) settings for clock delay */ +#define MEM_DLL_CLOCK_DELAY (1<<0) +/* Memory controller power down function */ +#define MEM_POWER_DOWN (1<<8) +/* Memory controller software reset */ +#define MEM_SOFT_RESET (1<<0) /* Status values */ diff --git a/include/linux/olpc-ec.h b/include/linux/olpc-ec.h index 5bb6e760aa61..2925df3ce78a 100644 --- a/include/linux/olpc-ec.h +++ b/include/linux/olpc-ec.h @@ -6,6 +6,7 @@ #define EC_WRITE_SCI_MASK 0x1b #define EC_WAKE_UP_WLAN 0x24 #define EC_WLAN_LEAVE_RESET 0x25 +#define EC_DCON_POWER_MODE 0x26 #define EC_READ_EB_MODE 0x2a #define EC_SET_SCI_INHIBIT 0x32 #define EC_SET_SCI_INHIBIT_RELEASE 0x34 -- cgit v1.2.3-71-gd317 From 6d33f7bb8c3863e54f8bdede0a2bf97a3585ac20 Mon Sep 17 00:00:00 2001 From: Thomas Pugliese Date: Thu, 15 Aug 2013 12:21:30 -0500 Subject: USB: WUSBCORE: clear RPIPE stall for control endpoints When the HWA encounters a STALL on a control endpoint, it should clear the RPIPE_STALL feature on the RPIPE before processing the next transfer request. Otherwise, all transfer requests on that endpoint after the first STALL will fail because the RPIPE is still in the halted state. This also removes the unneccessary call to spin_lock_irqsave for a nested lock that was present in the first patch. Signed-off-by: Thomas Pugliese Signed-off-by: Greg Kroah-Hartman --- drivers/usb/wusbcore/wa-hc.h | 15 +++++++- drivers/usb/wusbcore/wa-rpipe.c | 21 +++++++++++ drivers/usb/wusbcore/wa-xfer.c | 84 ++++++++++++++++++++++++++++++++++++----- include/linux/usb/wusb-wa.h | 1 + 4 files changed, 109 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/usb/wusbcore/wa-hc.h index d6bea3e0b54a..cf250c21e946 100644 --- a/drivers/usb/wusbcore/wa-hc.h +++ b/drivers/usb/wusbcore/wa-hc.h @@ -91,6 +91,7 @@ struct wusbhc; struct wahc; extern void wa_urb_enqueue_run(struct work_struct *ws); +extern void wa_process_errored_transfers_run(struct work_struct *ws); /** * RPipe instance @@ -190,8 +191,14 @@ struct wahc { struct list_head xfer_list; struct list_head xfer_delayed_list; + struct list_head xfer_errored_list; + /* + * lock for the above xfer lists. Can be taken while a xfer->lock is + * held but not in the reverse order. + */ spinlock_t xfer_list_lock; - struct work_struct xfer_work; + struct work_struct xfer_enqueue_work; + struct work_struct xfer_error_work; atomic_t xfer_id_count; }; @@ -244,8 +251,10 @@ static inline void wa_init(struct wahc *wa) edc_init(&wa->dti_edc); INIT_LIST_HEAD(&wa->xfer_list); INIT_LIST_HEAD(&wa->xfer_delayed_list); + INIT_LIST_HEAD(&wa->xfer_errored_list); spin_lock_init(&wa->xfer_list_lock); - INIT_WORK(&wa->xfer_work, wa_urb_enqueue_run); + INIT_WORK(&wa->xfer_enqueue_work, wa_urb_enqueue_run); + INIT_WORK(&wa->xfer_error_work, wa_process_errored_transfers_run); atomic_set(&wa->xfer_id_count, 1); } @@ -269,6 +278,8 @@ static inline void rpipe_put(struct wa_rpipe *rpipe) } extern void rpipe_ep_disable(struct wahc *, struct usb_host_endpoint *); +extern void rpipe_clear_feature_stalled(struct wahc *, + struct usb_host_endpoint *); extern int wa_rpipes_create(struct wahc *); extern void wa_rpipes_destroy(struct wahc *); static inline void rpipe_avail_dec(struct wa_rpipe *rpipe) diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c index 9a595c1ed867..fd4f1ce6256a 100644 --- a/drivers/usb/wusbcore/wa-rpipe.c +++ b/drivers/usb/wusbcore/wa-rpipe.c @@ -527,3 +527,24 @@ void rpipe_ep_disable(struct wahc *wa, struct usb_host_endpoint *ep) mutex_unlock(&wa->rpipe_mutex); } EXPORT_SYMBOL_GPL(rpipe_ep_disable); + +/* Clear the stalled status of an RPIPE. */ +void rpipe_clear_feature_stalled(struct wahc *wa, struct usb_host_endpoint *ep) +{ + struct wa_rpipe *rpipe; + + mutex_lock(&wa->rpipe_mutex); + rpipe = ep->hcpriv; + if (rpipe != NULL) { + u16 index = le16_to_cpu(rpipe->descr.wRPipeIndex); + + usb_control_msg( + wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE, + RPIPE_STALL, index, NULL, 0, 1000); + } + mutex_unlock(&wa->rpipe_mutex); +} +EXPORT_SYMBOL_GPL(rpipe_clear_feature_stalled); + diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index f5c81afc6e96..d74fe1ae16ac 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -1100,7 +1100,7 @@ error_xfer_submit: */ void wa_urb_enqueue_run(struct work_struct *ws) { - struct wahc *wa = container_of(ws, struct wahc, xfer_work); + struct wahc *wa = container_of(ws, struct wahc, xfer_enqueue_work); struct wa_xfer *xfer, *next; struct urb *urb; LIST_HEAD(tmp_list); @@ -1125,6 +1125,49 @@ void wa_urb_enqueue_run(struct work_struct *ws) } EXPORT_SYMBOL_GPL(wa_urb_enqueue_run); +/* + * Process the errored transfers on the Wire Adapter outside of interrupt. + */ +void wa_process_errored_transfers_run(struct work_struct *ws) +{ + struct wahc *wa = container_of(ws, struct wahc, xfer_error_work); + struct wa_xfer *xfer, *next; + LIST_HEAD(tmp_list); + + pr_info("%s: Run delayed STALL processing.\n", __func__); + + /* Create a copy of the wa->xfer_errored_list while holding the lock */ + spin_lock_irq(&wa->xfer_list_lock); + list_cut_position(&tmp_list, &wa->xfer_errored_list, + wa->xfer_errored_list.prev); + spin_unlock_irq(&wa->xfer_list_lock); + + /* + * run rpipe_clear_feature_stalled from temp list without list lock + * held. + */ + list_for_each_entry_safe(xfer, next, &tmp_list, list_node) { + struct usb_host_endpoint *ep; + unsigned long flags; + struct wa_rpipe *rpipe; + + spin_lock_irqsave(&xfer->lock, flags); + ep = xfer->ep; + rpipe = ep->hcpriv; + spin_unlock_irqrestore(&xfer->lock, flags); + + /* clear RPIPE feature stalled without holding a lock. */ + rpipe_clear_feature_stalled(wa, ep); + + /* complete the xfer. This removes it from the tmp list. */ + wa_xfer_completion(xfer); + + /* check for work. */ + wa_xfer_delayed_run(rpipe); + } +} +EXPORT_SYMBOL_GPL(wa_process_errored_transfers_run); + /* * Submit a transfer to the Wire Adapter in a delayed way * @@ -1180,7 +1223,7 @@ int wa_urb_enqueue(struct wahc *wa, struct usb_host_endpoint *ep, spin_lock_irqsave(&wa->xfer_list_lock, my_flags); list_add_tail(&xfer->list_node, &wa->xfer_delayed_list); spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags); - queue_work(wusbd, &wa->xfer_work); + queue_work(wusbd, &wa->xfer_enqueue_work); } else { wa_urb_enqueue_b(xfer); } @@ -1222,7 +1265,8 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb) xfer = urb->hcpriv; if (xfer == NULL) { - /* NOthing setup yet enqueue will see urb->status != + /* + * Nothing setup yet enqueue will see urb->status != * -EINPROGRESS (by hcd layer) and bail out with * error, no need to do completion */ @@ -1360,7 +1404,7 @@ static int wa_xfer_status_to_errno(u8 status) * * inbound transfers: need to schedule a DTI read * - * FIXME: this functio needs to be broken up in parts + * FIXME: this function needs to be broken up in parts */ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer) { @@ -1482,17 +1526,37 @@ error_submit_buf_in: seg->result = result; kfree(wa->buf_in_urb->sg); error_sg_alloc: + __wa_xfer_abort(xfer); error_complete: seg->status = WA_SEG_ERROR; xfer->segs_done++; rpipe_ready = rpipe_avail_inc(rpipe); - __wa_xfer_abort(xfer); done = __wa_xfer_is_done(xfer); - spin_unlock_irqrestore(&xfer->lock, flags); - if (done) - wa_xfer_completion(xfer); - if (rpipe_ready) - wa_xfer_delayed_run(rpipe); + /* + * queue work item to clear STALL for control endpoints. + * Otherwise, let endpoint_reset take care of it. + */ + if (((usb_status & 0x3f) == WA_XFER_STATUS_HALTED) && + usb_endpoint_xfer_control(&xfer->ep->desc) && + done) { + + dev_info(dev, "Control EP stall. Queue delayed work.\n"); + spin_lock_irq(&wa->xfer_list_lock); + /* remove xfer from xfer_list. */ + list_del(&xfer->list_node); + /* add xfer to xfer_errored_list. */ + list_add_tail(&xfer->list_node, &wa->xfer_errored_list); + spin_unlock_irq(&wa->xfer_list_lock); + spin_unlock_irqrestore(&xfer->lock, flags); + queue_work(wusbd, &wa->xfer_error_work); + } else { + spin_unlock_irqrestore(&xfer->lock, flags); + if (done) + wa_xfer_completion(xfer); + if (rpipe_ready) + wa_xfer_delayed_run(rpipe); + } + return; error_bad_seg: diff --git a/include/linux/usb/wusb-wa.h b/include/linux/usb/wusb-wa.h index 6be985b2a434..4ff744e2b678 100644 --- a/include/linux/usb/wusb-wa.h +++ b/include/linux/usb/wusb-wa.h @@ -66,6 +66,7 @@ enum { WA_ENABLE = 0x01, WA_RESET = 0x02, RPIPE_PAUSE = 0x1, + RPIPE_STALL = 0x2, }; /* Responses from Get Status request ([WUSB] section 8.3.1.6) */ -- cgit v1.2.3-71-gd317 From 74b77b1510c90787d4e2e3a8412b85b235590ba5 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Fri, 16 Aug 2013 11:45:33 +0200 Subject: ASoC: imx-audmux: Move definitions to dt-bindings Move imx-audmux macro definitions to include/dt-bindings, so they can be used for devicetree. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- include/dt-bindings/sound/fsl-imx-audmux.h | 56 ++++++++++++++++++++++++++++++ sound/soc/fsl/imx-audmux.h | 52 +-------------------------- 2 files changed, 57 insertions(+), 51 deletions(-) create mode 100644 include/dt-bindings/sound/fsl-imx-audmux.h (limited to 'include') diff --git a/include/dt-bindings/sound/fsl-imx-audmux.h b/include/dt-bindings/sound/fsl-imx-audmux.h new file mode 100644 index 000000000000..50b09e96f247 --- /dev/null +++ b/include/dt-bindings/sound/fsl-imx-audmux.h @@ -0,0 +1,56 @@ +#ifndef __DT_FSL_IMX_AUDMUX_H +#define __DT_FSL_IMX_AUDMUX_H + +#define MX27_AUDMUX_HPCR1_SSI0 0 +#define MX27_AUDMUX_HPCR2_SSI1 1 +#define MX27_AUDMUX_HPCR3_SSI_PINS_4 2 +#define MX27_AUDMUX_PPCR1_SSI_PINS_1 3 +#define MX27_AUDMUX_PPCR2_SSI_PINS_2 4 +#define MX27_AUDMUX_PPCR3_SSI_PINS_3 5 + +#define MX31_AUDMUX_PORT1_SSI0 0 +#define MX31_AUDMUX_PORT2_SSI1 1 +#define MX31_AUDMUX_PORT3_SSI_PINS_3 2 +#define MX31_AUDMUX_PORT4_SSI_PINS_4 3 +#define MX31_AUDMUX_PORT5_SSI_PINS_5 4 +#define MX31_AUDMUX_PORT6_SSI_PINS_6 5 +#define MX31_AUDMUX_PORT7_SSI_PINS_7 6 + +#define MX51_AUDMUX_PORT1_SSI0 0 +#define MX51_AUDMUX_PORT2_SSI1 1 +#define MX51_AUDMUX_PORT3 2 +#define MX51_AUDMUX_PORT4 3 +#define MX51_AUDMUX_PORT5 4 +#define MX51_AUDMUX_PORT6 5 +#define MX51_AUDMUX_PORT7 6 + +/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */ +#define IMX_AUDMUX_V1_PCR_INMMASK(x) ((x) & 0xff) +#define IMX_AUDMUX_V1_PCR_INMEN (1 << 8) +#define IMX_AUDMUX_V1_PCR_TXRXEN (1 << 10) +#define IMX_AUDMUX_V1_PCR_SYN (1 << 12) +#define IMX_AUDMUX_V1_PCR_RXDSEL(x) (((x) & 0x7) << 13) +#define IMX_AUDMUX_V1_PCR_RFCSEL(x) (((x) & 0xf) << 20) +#define IMX_AUDMUX_V1_PCR_RCLKDIR (1 << 24) +#define IMX_AUDMUX_V1_PCR_RFSDIR (1 << 25) +#define IMX_AUDMUX_V1_PCR_TFCSEL(x) (((x) & 0xf) << 26) +#define IMX_AUDMUX_V1_PCR_TCLKDIR (1 << 30) +#define IMX_AUDMUX_V1_PCR_TFSDIR (1 << 31) + +/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */ +#define IMX_AUDMUX_V2_PTCR_TFSDIR (1 << 31) +#define IMX_AUDMUX_V2_PTCR_TFSEL(x) (((x) & 0xf) << 27) +#define IMX_AUDMUX_V2_PTCR_TCLKDIR (1 << 26) +#define IMX_AUDMUX_V2_PTCR_TCSEL(x) (((x) & 0xf) << 22) +#define IMX_AUDMUX_V2_PTCR_RFSDIR (1 << 21) +#define IMX_AUDMUX_V2_PTCR_RFSEL(x) (((x) & 0xf) << 17) +#define IMX_AUDMUX_V2_PTCR_RCLKDIR (1 << 16) +#define IMX_AUDMUX_V2_PTCR_RCSEL(x) (((x) & 0xf) << 12) +#define IMX_AUDMUX_V2_PTCR_SYN (1 << 11) + +#define IMX_AUDMUX_V2_PDCR_RXDSEL(x) (((x) & 0x7) << 13) +#define IMX_AUDMUX_V2_PDCR_TXRXEN (1 << 12) +#define IMX_AUDMUX_V2_PDCR_MODE(x) (((x) & 0x3) << 8) +#define IMX_AUDMUX_V2_PDCR_INMMASK(x) ((x) & 0xff) + +#endif /* __DT_FSL_IMX_AUDMUX_H */ diff --git a/sound/soc/fsl/imx-audmux.h b/sound/soc/fsl/imx-audmux.h index b8ff44b9dafa..38a4209af7c6 100644 --- a/sound/soc/fsl/imx-audmux.h +++ b/sound/soc/fsl/imx-audmux.h @@ -1,57 +1,7 @@ #ifndef __IMX_AUDMUX_H #define __IMX_AUDMUX_H -#define MX27_AUDMUX_HPCR1_SSI0 0 -#define MX27_AUDMUX_HPCR2_SSI1 1 -#define MX27_AUDMUX_HPCR3_SSI_PINS_4 2 -#define MX27_AUDMUX_PPCR1_SSI_PINS_1 3 -#define MX27_AUDMUX_PPCR2_SSI_PINS_2 4 -#define MX27_AUDMUX_PPCR3_SSI_PINS_3 5 - -#define MX31_AUDMUX_PORT1_SSI0 0 -#define MX31_AUDMUX_PORT2_SSI1 1 -#define MX31_AUDMUX_PORT3_SSI_PINS_3 2 -#define MX31_AUDMUX_PORT4_SSI_PINS_4 3 -#define MX31_AUDMUX_PORT5_SSI_PINS_5 4 -#define MX31_AUDMUX_PORT6_SSI_PINS_6 5 -#define MX31_AUDMUX_PORT7_SSI_PINS_7 6 - -#define MX51_AUDMUX_PORT1_SSI0 0 -#define MX51_AUDMUX_PORT2_SSI1 1 -#define MX51_AUDMUX_PORT3 2 -#define MX51_AUDMUX_PORT4 3 -#define MX51_AUDMUX_PORT5 4 -#define MX51_AUDMUX_PORT6 5 -#define MX51_AUDMUX_PORT7 6 - -/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */ -#define IMX_AUDMUX_V1_PCR_INMMASK(x) ((x) & 0xff) -#define IMX_AUDMUX_V1_PCR_INMEN (1 << 8) -#define IMX_AUDMUX_V1_PCR_TXRXEN (1 << 10) -#define IMX_AUDMUX_V1_PCR_SYN (1 << 12) -#define IMX_AUDMUX_V1_PCR_RXDSEL(x) (((x) & 0x7) << 13) -#define IMX_AUDMUX_V1_PCR_RFCSEL(x) (((x) & 0xf) << 20) -#define IMX_AUDMUX_V1_PCR_RCLKDIR (1 << 24) -#define IMX_AUDMUX_V1_PCR_RFSDIR (1 << 25) -#define IMX_AUDMUX_V1_PCR_TFCSEL(x) (((x) & 0xf) << 26) -#define IMX_AUDMUX_V1_PCR_TCLKDIR (1 << 30) -#define IMX_AUDMUX_V1_PCR_TFSDIR (1 << 31) - -/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */ -#define IMX_AUDMUX_V2_PTCR_TFSDIR (1 << 31) -#define IMX_AUDMUX_V2_PTCR_TFSEL(x) (((x) & 0xf) << 27) -#define IMX_AUDMUX_V2_PTCR_TCLKDIR (1 << 26) -#define IMX_AUDMUX_V2_PTCR_TCSEL(x) (((x) & 0xf) << 22) -#define IMX_AUDMUX_V2_PTCR_RFSDIR (1 << 21) -#define IMX_AUDMUX_V2_PTCR_RFSEL(x) (((x) & 0xf) << 17) -#define IMX_AUDMUX_V2_PTCR_RCLKDIR (1 << 16) -#define IMX_AUDMUX_V2_PTCR_RCSEL(x) (((x) & 0xf) << 12) -#define IMX_AUDMUX_V2_PTCR_SYN (1 << 11) - -#define IMX_AUDMUX_V2_PDCR_RXDSEL(x) (((x) & 0x7) << 13) -#define IMX_AUDMUX_V2_PDCR_TXRXEN (1 << 12) -#define IMX_AUDMUX_V2_PDCR_MODE(x) (((x) & 0x3) << 8) -#define IMX_AUDMUX_V2_PDCR_INMMASK(x) ((x) & 0xff) +#include int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr); -- cgit v1.2.3-71-gd317 From 27b3eb9c06a7193bdc9800cd00764a130343bc8a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 7 Aug 2013 20:11:55 +0200 Subject: mac80211: add APIs to allow keeping connections after WoWLAN In order to be able to (securely) keep connections alive after the system was suspended for WoWLAN, we need some additional APIs. We already have API (ieee80211_gtk_rekey_notify) to tell wpa_supplicant about the new replay counter if GTK rekeying was done by the device while the host was asleep, but that's not sufficient. If GTK rekeying wasn't done, we need to tell the host about sequence counters for the GTK (and PTK regardless of rekeying) that was used while asleep, add ieee80211_set_key_rx_seq() for that. If GTK rekeying was done, then we need to be able to disable the old keys (with ieee80211_remove_key()) and allocate the new GTK key(s) in mac80211 (with ieee80211_gtk_rekey_add()). If protocol offload (e.g. ARP) is implemented, then also the TX sequence counter for the PTK must be updated, using the new ieee80211_set_key_tx_seq() function. Signed-off-by: Johannes Berg --- include/net/mac80211.h | 83 ++++++++++++++++++++++++++ net/mac80211/key.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++--- net/mac80211/util.c | 2 +- 3 files changed, 231 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index df93c77c97ab..e3e303778936 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3687,6 +3687,89 @@ void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf, void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, int tid, struct ieee80211_key_seq *seq); +/** + * ieee80211_set_key_tx_seq - set key TX sequence counter + * + * @keyconf: the parameter passed with the set key + * @seq: new sequence data + * + * This function allows a driver to set the current TX IV/PNs for the + * given key. This is useful when resuming from WoWLAN sleep and the + * device may have transmitted frames using the PTK, e.g. replies to + * ARP requests. + * + * Note that this function may only be called when no TX processing + * can be done concurrently. + */ +void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf, + struct ieee80211_key_seq *seq); + +/** + * ieee80211_set_key_rx_seq - set key RX sequence counter + * + * @keyconf: the parameter passed with the set key + * @tid: The TID, or -1 for the management frame value (CCMP only); + * the value on TID 0 is also used for non-QoS frames. For + * CMAC, only TID 0 is valid. + * @seq: new sequence data + * + * This function allows a driver to set the current RX IV/PNs for the + * given key. This is useful when resuming from WoWLAN sleep and GTK + * rekey may have been done while suspended. It should not be called + * if IV checking is done by the device and not by mac80211. + * + * Note that this function may only be called when no RX processing + * can be done concurrently. + */ +void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf, + int tid, struct ieee80211_key_seq *seq); + +/** + * ieee80211_remove_key - remove the given key + * @keyconf: the parameter passed with the set key + * + * Remove the given key. If the key was uploaded to the hardware at the + * time this function is called, it is not deleted in the hardware but + * instead assumed to have been removed already. + * + * Note that due to locking considerations this function can (currently) + * only be called during key iteration (ieee80211_iter_keys().) + */ +void ieee80211_remove_key(struct ieee80211_key_conf *keyconf); + +/** + * ieee80211_gtk_rekey_add - add a GTK key from rekeying during WoWLAN + * @vif: the virtual interface to add the key on + * @keyconf: new key data + * + * When GTK rekeying was done while the system was suspended, (a) new + * key(s) will be available. These will be needed by mac80211 for proper + * RX processing, so this function allows setting them. + * + * The function returns the newly allocated key structure, which will + * have similar contents to the passed key configuration but point to + * mac80211-owned memory. In case of errors, the function returns an + * ERR_PTR(), use IS_ERR() etc. + * + * Note that this function assumes the key isn't added to hardware + * acceleration, so no TX will be done with the key. Since it's a GTK + * on managed (station) networks, this is true anyway. If the driver + * calls this function from the resume callback and subsequently uses + * the return code 1 to reconfigure the device, this key will be part + * of the reconfiguration. + * + * Note that the driver should also call ieee80211_set_key_rx_seq() + * for the new key for each TID to set up sequence counters properly. + * + * IMPORTANT: If this replaces a key that is present in the hardware, + * then it will attempt to remove it during this call. In many cases + * this isn't what you want, so call ieee80211_remove_key() first for + * the key that's being replaced. + */ +struct ieee80211_key_conf * +ieee80211_gtk_rekey_add(struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf); + /** * ieee80211_gtk_rekey_notify - notify userspace supplicant of rekeying * @vif: virtual interface the rekeying was done on diff --git a/net/mac80211/key.c b/net/mac80211/key.c index e39cc91d0cf1..620677e897bd 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -93,6 +93,9 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) might_sleep(); + if (key->flags & KEY_FLAG_TAINTED) + return -EINVAL; + if (!key->local->ops->set_key) goto out_unsupported; @@ -455,6 +458,7 @@ int ieee80211_key_link(struct ieee80211_key *key, struct ieee80211_sub_if_data *sdata, struct sta_info *sta) { + struct ieee80211_local *local = sdata->local; struct ieee80211_key *old_key; int idx, ret; bool pairwise; @@ -484,10 +488,13 @@ int ieee80211_key_link(struct ieee80211_key *key, ieee80211_debugfs_key_add(key); - ret = ieee80211_key_enable_hw_accel(key); - - if (ret) - ieee80211_key_free(key, true); + if (!local->wowlan) { + ret = ieee80211_key_enable_hw_accel(key); + if (ret) + ieee80211_key_free(key, true); + } else { + ret = 0; + } mutex_unlock(&sdata->local->key_mtx); @@ -540,7 +547,7 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw, void *iter_data) { struct ieee80211_local *local = hw_to_local(hw); - struct ieee80211_key *key; + struct ieee80211_key *key, *tmp; struct ieee80211_sub_if_data *sdata; ASSERT_RTNL(); @@ -548,13 +555,14 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw, mutex_lock(&local->key_mtx); if (vif) { sdata = vif_to_sdata(vif); - list_for_each_entry(key, &sdata->key_list, list) + list_for_each_entry_safe(key, tmp, &sdata->key_list, list) iter(hw, &sdata->vif, key->sta ? &key->sta->sta : NULL, &key->conf, iter_data); } else { list_for_each_entry(sdata, &local->interfaces, list) - list_for_each_entry(key, &sdata->key_list, list) + list_for_each_entry_safe(key, tmp, + &sdata->key_list, list) iter(hw, &sdata->vif, key->sta ? &key->sta->sta : NULL, &key->conf, iter_data); @@ -751,3 +759,135 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, } } EXPORT_SYMBOL(ieee80211_get_key_rx_seq); + +void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf, + struct ieee80211_key_seq *seq) +{ + struct ieee80211_key *key; + u64 pn64; + + key = container_of(keyconf, struct ieee80211_key, conf); + + switch (key->conf.cipher) { + case WLAN_CIPHER_SUITE_TKIP: + key->u.tkip.tx.iv32 = seq->tkip.iv32; + key->u.tkip.tx.iv16 = seq->tkip.iv16; + break; + case WLAN_CIPHER_SUITE_CCMP: + pn64 = (u64)seq->ccmp.pn[5] | + ((u64)seq->ccmp.pn[4] << 8) | + ((u64)seq->ccmp.pn[3] << 16) | + ((u64)seq->ccmp.pn[2] << 24) | + ((u64)seq->ccmp.pn[1] << 32) | + ((u64)seq->ccmp.pn[0] << 40); + atomic64_set(&key->u.ccmp.tx_pn, pn64); + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + pn64 = (u64)seq->aes_cmac.pn[5] | + ((u64)seq->aes_cmac.pn[4] << 8) | + ((u64)seq->aes_cmac.pn[3] << 16) | + ((u64)seq->aes_cmac.pn[2] << 24) | + ((u64)seq->aes_cmac.pn[1] << 32) | + ((u64)seq->aes_cmac.pn[0] << 40); + atomic64_set(&key->u.aes_cmac.tx_pn, pn64); + break; + default: + WARN_ON(1); + break; + } +} +EXPORT_SYMBOL_GPL(ieee80211_set_key_tx_seq); + +void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf, + int tid, struct ieee80211_key_seq *seq) +{ + struct ieee80211_key *key; + u8 *pn; + + key = container_of(keyconf, struct ieee80211_key, conf); + + switch (key->conf.cipher) { + case WLAN_CIPHER_SUITE_TKIP: + if (WARN_ON(tid < 0 || tid >= IEEE80211_NUM_TIDS)) + return; + key->u.tkip.rx[tid].iv32 = seq->tkip.iv32; + key->u.tkip.rx[tid].iv16 = seq->tkip.iv16; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS)) + return; + if (tid < 0) + pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS]; + else + pn = key->u.ccmp.rx_pn[tid]; + memcpy(pn, seq->ccmp.pn, IEEE80211_CCMP_PN_LEN); + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + if (WARN_ON(tid != 0)) + return; + pn = key->u.aes_cmac.rx_pn; + memcpy(pn, seq->aes_cmac.pn, IEEE80211_CMAC_PN_LEN); + break; + default: + WARN_ON(1); + break; + } +} +EXPORT_SYMBOL_GPL(ieee80211_set_key_rx_seq); + +void ieee80211_remove_key(struct ieee80211_key_conf *keyconf) +{ + struct ieee80211_key *key; + + key = container_of(keyconf, struct ieee80211_key, conf); + + assert_key_lock(key->local); + + /* + * if key was uploaded, we assume the driver will/has remove(d) + * it, so adjust bookkeeping accordingly + */ + if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) { + key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; + + if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || + (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || + (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) + increment_tailroom_need_count(key->sdata); + } + + ieee80211_key_free(key, false); +} +EXPORT_SYMBOL_GPL(ieee80211_remove_key); + +struct ieee80211_key_conf * +ieee80211_gtk_rekey_add(struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_key *key; + int err; + + if (WARN_ON(!local->wowlan)) + return ERR_PTR(-EINVAL); + + if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) + return ERR_PTR(-EINVAL); + + key = ieee80211_key_alloc(keyconf->cipher, keyconf->keyidx, + keyconf->keylen, keyconf->key, + 0, NULL); + if (IS_ERR(key)) + return ERR_PTR(PTR_ERR(key)); + + if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED) + key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; + + err = ieee80211_key_link(key, sdata, NULL); + if (err) + return ERR_PTR(err); + + return &key->conf; +} +EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_add); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index d23c5a705a68..e1b34a18b243 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1453,8 +1453,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) local->resuming = true; if (local->wowlan) { - local->wowlan = false; res = drv_resume(local); + local->wowlan = false; if (res < 0) { local->resuming = false; return res; -- cgit v1.2.3-71-gd317 From 3be78c73179c9347bdc0a92b2898063bd2300ff7 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 16 Aug 2013 21:22:41 -0400 Subject: ext4: use unsigned int for es_status values Don't use an unsigned long long for the es_status flags; this requires that we pass 64-bit values around which is painful on 32-bit systems. Instead pass the extent status flags around using the low 4 bits of an unsigned int, and shift them into place when we are reading or writing es_pblk. Signed-off-by: "Theodore Ts'o" Reviewed-by: Zheng Liu --- fs/ext4/extents_status.c | 6 +++--- fs/ext4/extents_status.h | 47 ++++++++++++++++++++++++++------------------- fs/ext4/inode.c | 6 +++--- include/trace/events/ext4.h | 14 +++++++------- 4 files changed, 40 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index 91cb110da1b4..ded2615b63e0 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -263,7 +263,7 @@ void ext4_es_find_delayed_extent_range(struct inode *inode, if (tree->cache_es) { es1 = tree->cache_es; if (in_range(lblk, es1->es_lblk, es1->es_len)) { - es_debug("%u cached by [%u/%u) %llu %llx\n", + es_debug("%u cached by [%u/%u) %llu %x\n", lblk, es1->es_lblk, es1->es_len, ext4_es_pblock(es1), ext4_es_status(es1)); goto out; @@ -641,13 +641,13 @@ out: */ int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len, ext4_fsblk_t pblk, - unsigned long long status) + unsigned int status) { struct extent_status newes; ext4_lblk_t end = lblk + len - 1; int err = 0; - es_debug("add [%u/%u) %llu %llx to extent status tree of inode %lu\n", + es_debug("add [%u/%u) %llu %x to extent status tree of inode %lu\n", lblk, len, pblk, status, inode->i_ino); if (!len) diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h index e936730cc5b0..d72af848f989 100644 --- a/fs/ext4/extents_status.h +++ b/fs/ext4/extents_status.h @@ -29,16 +29,26 @@ /* * These flags live in the high bits of extent_status.es_pblk */ -#define EXTENT_STATUS_WRITTEN (1ULL << 63) -#define EXTENT_STATUS_UNWRITTEN (1ULL << 62) -#define EXTENT_STATUS_DELAYED (1ULL << 61) -#define EXTENT_STATUS_HOLE (1ULL << 60) +#define ES_SHIFT 60 + +#define EXTENT_STATUS_WRITTEN (1 << 3) +#define EXTENT_STATUS_UNWRITTEN (1 << 2) +#define EXTENT_STATUS_DELAYED (1 << 1) +#define EXTENT_STATUS_HOLE (1 << 0) #define EXTENT_STATUS_FLAGS (EXTENT_STATUS_WRITTEN | \ EXTENT_STATUS_UNWRITTEN | \ EXTENT_STATUS_DELAYED | \ EXTENT_STATUS_HOLE) +#define ES_WRITTEN (1ULL << 63) +#define ES_UNWRITTEN (1ULL << 62) +#define ES_DELAYED (1ULL << 61) +#define ES_HOLE (1ULL << 60) + +#define ES_MASK (ES_WRITTEN | ES_UNWRITTEN | \ + ES_DELAYED | ES_HOLE) + struct ext4_sb_info; struct ext4_extent; @@ -60,7 +70,7 @@ extern void ext4_es_init_tree(struct ext4_es_tree *tree); extern int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len, ext4_fsblk_t pblk, - unsigned long long status); + unsigned int status); extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len); extern void ext4_es_find_delayed_extent_range(struct inode *inode, @@ -72,32 +82,32 @@ extern int ext4_es_zeroout(struct inode *inode, struct ext4_extent *ex); static inline int ext4_es_is_written(struct extent_status *es) { - return (es->es_pblk & EXTENT_STATUS_WRITTEN) != 0; + return (es->es_pblk & ES_WRITTEN) != 0; } static inline int ext4_es_is_unwritten(struct extent_status *es) { - return (es->es_pblk & EXTENT_STATUS_UNWRITTEN) != 0; + return (es->es_pblk & ES_UNWRITTEN) != 0; } static inline int ext4_es_is_delayed(struct extent_status *es) { - return (es->es_pblk & EXTENT_STATUS_DELAYED) != 0; + return (es->es_pblk & ES_DELAYED) != 0; } static inline int ext4_es_is_hole(struct extent_status *es) { - return (es->es_pblk & EXTENT_STATUS_HOLE) != 0; + return (es->es_pblk & ES_HOLE) != 0; } -static inline ext4_fsblk_t ext4_es_status(struct extent_status *es) +static inline unsigned int ext4_es_status(struct extent_status *es) { - return (es->es_pblk & EXTENT_STATUS_FLAGS); + return es->es_pblk >> ES_SHIFT; } static inline ext4_fsblk_t ext4_es_pblock(struct extent_status *es) { - return (es->es_pblk & ~EXTENT_STATUS_FLAGS); + return es->es_pblk & ~ES_MASK; } static inline void ext4_es_store_pblock(struct extent_status *es, @@ -105,19 +115,16 @@ static inline void ext4_es_store_pblock(struct extent_status *es, { ext4_fsblk_t block; - block = (pb & ~EXTENT_STATUS_FLAGS) | - (es->es_pblk & EXTENT_STATUS_FLAGS); + block = (pb & ~ES_MASK) | (es->es_pblk & ES_MASK); es->es_pblk = block; } static inline void ext4_es_store_status(struct extent_status *es, - unsigned long long status) + unsigned int status) { - ext4_fsblk_t block; - - block = (status & EXTENT_STATUS_FLAGS) | - (es->es_pblk & ~EXTENT_STATUS_FLAGS); - es->es_pblk = block; + es->es_pblk = (((ext4_fsblk_t) + (status & EXTENT_STATUS_FLAGS) << ES_SHIFT) | + (es->es_pblk & ~ES_MASK)); } extern void ext4_es_register_shrinker(struct ext4_sb_info *sbi); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index c2ca04e67a4f..0569c745475c 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -553,7 +553,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, } if (retval > 0) { int ret; - unsigned long long status; + unsigned int status; if (unlikely(retval != map->m_len)) { ext4_warning(inode->i_sb, @@ -653,7 +653,7 @@ found: if (retval > 0) { int ret; - unsigned long long status; + unsigned int status; if (unlikely(retval != map->m_len)) { ext4_warning(inode->i_sb, @@ -1633,7 +1633,7 @@ add_delayed: set_buffer_delay(bh); } else if (retval > 0) { int ret; - unsigned long long status; + unsigned int status; if (unlikely(retval != map->m_len)) { ext4_warning(inode->i_sb, diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 2068db241f22..47a355b251e4 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -64,10 +64,10 @@ struct extent_status; { EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER, "LAST_CLUSTER" }) #define show_extent_status(status) __print_flags(status, "", \ - { (1 << 3), "W" }, \ - { (1 << 2), "U" }, \ - { (1 << 1), "D" }, \ - { (1 << 0), "H" }) + { EXTENT_STATUS_WRITTEN, "W" }, \ + { EXTENT_STATUS_UNWRITTEN, "U" }, \ + { EXTENT_STATUS_DELAYED, "D" }, \ + { EXTENT_STATUS_HOLE, "H" }) TRACE_EVENT(ext4_free_inode, @@ -2212,7 +2212,7 @@ TRACE_EVENT(ext4_es_insert_extent, __entry->lblk = es->es_lblk; __entry->len = es->es_len; __entry->pblk = ext4_es_pblock(es); - __entry->status = ext4_es_status(es) >> 60; + __entry->status = ext4_es_status(es); ), TP_printk("dev %d,%d ino %lu es [%u/%u) mapped %llu status %s", @@ -2289,7 +2289,7 @@ TRACE_EVENT(ext4_es_find_delayed_extent_range_exit, __entry->lblk = es->es_lblk; __entry->len = es->es_len; __entry->pblk = ext4_es_pblock(es); - __entry->status = ext4_es_status(es) >> 60; + __entry->status = ext4_es_status(es); ), TP_printk("dev %d,%d ino %lu es [%u/%u) mapped %llu status %s", @@ -2343,7 +2343,7 @@ TRACE_EVENT(ext4_es_lookup_extent_exit, __entry->lblk = es->es_lblk; __entry->len = es->es_len; __entry->pblk = ext4_es_pblock(es); - __entry->status = ext4_es_status(es) >> 60; + __entry->status = ext4_es_status(es); __entry->found = found; ), -- cgit v1.2.3-71-gd317 From 107a7bd31ac003e42c0f966aa8e5b26947de6024 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 16 Aug 2013 21:23:41 -0400 Subject: ext4: cache all of an extent tree's leaf block upon reading When we read in an extent tree leaf block from disk, arrange to have all of its entries cached. In nearly all cases the in-memory representation will be more compact than the on-disk representation in the buffer cache, and it allows us to get the information without having to traverse the extent tree for successive extents. Signed-off-by: "Theodore Ts'o" Reviewed-by: Zheng Liu --- fs/ext4/ext4.h | 14 +++++++- fs/ext4/extents.c | 87 +++++++++++++++++++++++++++++++-------------- fs/ext4/extents_status.c | 37 ++++++++++++++++++- fs/ext4/extents_status.h | 3 ++ fs/ext4/migrate.c | 2 +- fs/ext4/move_extent.c | 2 +- include/trace/events/ext4.h | 14 +++++++- 7 files changed, 127 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 0ab26fbf3380..c74b1948feb0 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -560,6 +560,17 @@ enum { /* Do not put hole in extent cache */ #define EXT4_GET_BLOCKS_NO_PUT_HOLE 0x0200 +/* + * The bit position of this flag must not overlap with any of the + * EXT4_GET_BLOCKS_*. It is used by ext4_ext_find_extent(), + * read_extent_tree_block(), ext4_split_extent_at(), + * ext4_ext_insert_extent(), and ext4_ext_create_new_leaf() to + * indicate that the we shouldn't be caching the extents when reading + * from the extent tree while a truncate or punch hole operation + * is in progress. + */ +#define EXT4_EX_NOCACHE 0x0400 + /* * Flags used by ext4_free_blocks */ @@ -2684,7 +2695,8 @@ extern int ext4_ext_insert_extent(handle_t *, struct inode *, struct ext4_ext_path *, struct ext4_extent *, int); extern struct ext4_ext_path *ext4_ext_find_extent(struct inode *, ext4_lblk_t, - struct ext4_ext_path *); + struct ext4_ext_path *, + int flags); extern void ext4_ext_drop_refs(struct ext4_ext_path *); extern int ext4_ext_check_inode(struct inode *inode); extern int ext4_find_delalloc_range(struct inode *inode, diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 6e7b7d928f4a..08c1ac976479 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -466,7 +466,8 @@ int ext4_ext_check_inode(struct inode *inode) static struct buffer_head * __read_extent_tree_block(const char *function, unsigned int line, - struct inode *inode, ext4_fsblk_t pblk, int depth) + struct inode *inode, ext4_fsblk_t pblk, int depth, + int flags) { struct buffer_head *bh; int err; @@ -488,6 +489,32 @@ __read_extent_tree_block(const char *function, unsigned int line, if (err) goto errout; set_buffer_verified(bh); + /* + * If this is a leaf block, cache all of its entries + */ + if (!(flags & EXT4_EX_NOCACHE) && depth == 0) { + struct ext4_extent_header *eh = ext_block_hdr(bh); + struct ext4_extent *ex = EXT_FIRST_EXTENT(eh); + ext4_lblk_t prev = 0; + int i; + + for (i = le16_to_cpu(eh->eh_entries); i > 0; i--, ex++) { + unsigned int status = EXTENT_STATUS_WRITTEN; + ext4_lblk_t lblk = le32_to_cpu(ex->ee_block); + int len = ext4_ext_get_actual_len(ex); + + if (prev && (prev != lblk)) + ext4_es_cache_extent(inode, prev, + lblk - prev, ~0, + EXTENT_STATUS_HOLE); + + if (ext4_ext_is_uninitialized(ex)) + status = EXTENT_STATUS_UNWRITTEN; + ext4_es_cache_extent(inode, lblk, len, + ext4_ext_pblock(ex), status); + prev = lblk + len; + } + } return bh; errout: put_bh(bh); @@ -495,8 +522,9 @@ errout: } -#define read_extent_tree_block(inode, pblk, depth) \ - __read_extent_tree_block(__func__, __LINE__, (inode), (pblk), (depth)) +#define read_extent_tree_block(inode, pblk, depth, flags) \ + __read_extent_tree_block(__func__, __LINE__, (inode), (pblk), \ + (depth), (flags)) #ifdef EXT_DEBUG static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path) @@ -730,7 +758,7 @@ int ext4_ext_tree_init(handle_t *handle, struct inode *inode) struct ext4_ext_path * ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block, - struct ext4_ext_path *path) + struct ext4_ext_path *path, int flags) { struct ext4_extent_header *eh; struct buffer_head *bh; @@ -762,7 +790,8 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block, path[ppos].p_depth = i; path[ppos].p_ext = NULL; - bh = read_extent_tree_block(inode, path[ppos].p_block, --i); + bh = read_extent_tree_block(inode, path[ppos].p_block, --i, + flags); if (IS_ERR(bh)) { ret = PTR_ERR(bh); goto err; @@ -1199,7 +1228,8 @@ out: * if no free index is found, then it requests in-depth growing. */ static int ext4_ext_create_new_leaf(handle_t *handle, struct inode *inode, - unsigned int flags, + unsigned int mb_flags, + unsigned int gb_flags, struct ext4_ext_path *path, struct ext4_extent *newext) { @@ -1221,7 +1251,7 @@ repeat: if (EXT_HAS_FREE_INDEX(curp)) { /* if we found index with free entry, then use that * entry: create all needed subtree and add new leaf */ - err = ext4_ext_split(handle, inode, flags, path, newext, i); + err = ext4_ext_split(handle, inode, mb_flags, path, newext, i); if (err) goto out; @@ -1229,12 +1259,12 @@ repeat: ext4_ext_drop_refs(path); path = ext4_ext_find_extent(inode, (ext4_lblk_t)le32_to_cpu(newext->ee_block), - path); + path, gb_flags); if (IS_ERR(path)) err = PTR_ERR(path); } else { /* tree is full, time to grow in depth */ - err = ext4_ext_grow_indepth(handle, inode, flags, newext); + err = ext4_ext_grow_indepth(handle, inode, mb_flags, newext); if (err) goto out; @@ -1242,7 +1272,7 @@ repeat: ext4_ext_drop_refs(path); path = ext4_ext_find_extent(inode, (ext4_lblk_t)le32_to_cpu(newext->ee_block), - path); + path, gb_flags); if (IS_ERR(path)) { err = PTR_ERR(path); goto out; @@ -1415,7 +1445,7 @@ got_index: while (++depth < path->p_depth) { /* subtract from p_depth to get proper eh_depth */ bh = read_extent_tree_block(inode, block, - path->p_depth - depth); + path->p_depth - depth, 0); if (IS_ERR(bh)) return PTR_ERR(bh); eh = ext_block_hdr(bh); @@ -1424,7 +1454,7 @@ got_index: put_bh(bh); } - bh = read_extent_tree_block(inode, block, path->p_depth - depth); + bh = read_extent_tree_block(inode, block, path->p_depth - depth, 0); if (IS_ERR(bh)) return PTR_ERR(bh); eh = ext_block_hdr(bh); @@ -1786,7 +1816,7 @@ out: */ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode, struct ext4_ext_path *path, - struct ext4_extent *newext, int flag) + struct ext4_extent *newext, int gb_flags) { struct ext4_extent_header *eh; struct ext4_extent *ex, *fex; @@ -1795,7 +1825,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode, int depth, len, err; ext4_lblk_t next; unsigned uninitialized = 0; - int flags = 0; + int mb_flags = 0; if (unlikely(ext4_ext_get_actual_len(newext) == 0)) { EXT4_ERROR_INODE(inode, "ext4_ext_get_actual_len(newext) == 0"); @@ -1810,7 +1840,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode, } /* try to insert block into found extent and return */ - if (ex && !(flag & EXT4_GET_BLOCKS_PRE_IO)) { + if (ex && !(gb_flags & EXT4_GET_BLOCKS_PRE_IO)) { /* * Try to see whether we should rather test the extent on @@ -1913,7 +1943,7 @@ prepend: if (next != EXT_MAX_BLOCKS) { ext_debug("next leaf block - %u\n", next); BUG_ON(npath != NULL); - npath = ext4_ext_find_extent(inode, next, NULL); + npath = ext4_ext_find_extent(inode, next, NULL, 0); if (IS_ERR(npath)) return PTR_ERR(npath); BUG_ON(npath->p_depth != path->p_depth); @@ -1932,9 +1962,10 @@ prepend: * There is no free space in the found leaf. * We're gonna add a new leaf in the tree. */ - if (flag & EXT4_GET_BLOCKS_METADATA_NOFAIL) - flags = EXT4_MB_USE_RESERVED; - err = ext4_ext_create_new_leaf(handle, inode, flags, path, newext); + if (gb_flags & EXT4_GET_BLOCKS_METADATA_NOFAIL) + mb_flags = EXT4_MB_USE_RESERVED; + err = ext4_ext_create_new_leaf(handle, inode, mb_flags, gb_flags, + path, newext); if (err) goto cleanup; depth = ext_depth(inode); @@ -2000,7 +2031,7 @@ has_space: merge: /* try to merge extents */ - if (!(flag & EXT4_GET_BLOCKS_PRE_IO)) + if (!(gb_flags & EXT4_GET_BLOCKS_PRE_IO)) ext4_ext_try_to_merge(handle, inode, path, nearex); @@ -2043,7 +2074,7 @@ static int ext4_fill_fiemap_extents(struct inode *inode, path = NULL; } - path = ext4_ext_find_extent(inode, block, path); + path = ext4_ext_find_extent(inode, block, path, 0); if (IS_ERR(path)) { up_read(&EXT4_I(inode)->i_data_sem); err = PTR_ERR(path); @@ -2705,7 +2736,7 @@ again: ext4_lblk_t ee_block; /* find extent for this block */ - path = ext4_ext_find_extent(inode, end, NULL); + path = ext4_ext_find_extent(inode, end, NULL, EXT4_EX_NOCACHE); if (IS_ERR(path)) { ext4_journal_stop(handle); return PTR_ERR(path); @@ -2747,6 +2778,7 @@ again: */ err = ext4_split_extent_at(handle, inode, path, end + 1, split_flag, + EXT4_EX_NOCACHE | EXT4_GET_BLOCKS_PRE_IO | EXT4_GET_BLOCKS_METADATA_NOFAIL); @@ -2823,7 +2855,8 @@ again: i + 1, ext4_idx_pblock(path[i].p_idx)); memset(path + i + 1, 0, sizeof(*path)); bh = read_extent_tree_block(inode, - ext4_idx_pblock(path[i].p_idx), depth - i - 1); + ext4_idx_pblock(path[i].p_idx), depth - i - 1, + EXT4_EX_NOCACHE); if (IS_ERR(bh)) { /* should we reset i_size? */ err = PTR_ERR(bh); @@ -3170,7 +3203,7 @@ static int ext4_split_extent(handle_t *handle, * result in split of original leaf or extent zeroout. */ ext4_ext_drop_refs(path); - path = ext4_ext_find_extent(inode, map->m_lblk, path); + path = ext4_ext_find_extent(inode, map->m_lblk, path, 0); if (IS_ERR(path)) return PTR_ERR(path); depth = ext_depth(inode); @@ -3554,7 +3587,7 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle, if (err < 0) goto out; ext4_ext_drop_refs(path); - path = ext4_ext_find_extent(inode, map->m_lblk, path); + path = ext4_ext_find_extent(inode, map->m_lblk, path, 0); if (IS_ERR(path)) { err = PTR_ERR(path); goto out; @@ -4041,7 +4074,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, trace_ext4_ext_map_blocks_enter(inode, map->m_lblk, map->m_len, flags); /* find extent for this block */ - path = ext4_ext_find_extent(inode, map->m_lblk, NULL); + path = ext4_ext_find_extent(inode, map->m_lblk, NULL, 0); if (IS_ERR(path)) { err = PTR_ERR(path); path = NULL; @@ -4760,6 +4793,6 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, error = ext4_fill_fiemap_extents(inode, start_blk, len_blks, fieinfo); } - + ext4_es_lru_add(inode); return error; } diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index ded2615b63e0..1dc5df016e25 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -419,7 +419,7 @@ static void ext4_es_insert_extent_ext_check(struct inode *inode, unsigned short ee_len; int depth, ee_status, es_status; - path = ext4_ext_find_extent(inode, es->es_lblk, NULL); + path = ext4_ext_find_extent(inode, es->es_lblk, NULL, EXT4_EX_NOCACHE); if (IS_ERR(path)) return; @@ -683,6 +683,41 @@ error: return err; } +/* + * ext4_es_cache_extent() inserts information into the extent status + * tree if and only if there isn't information about the range in + * question already. + */ +void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk, + ext4_lblk_t len, ext4_fsblk_t pblk, + unsigned int status) +{ + struct extent_status *es; + struct extent_status newes; + ext4_lblk_t end = lblk + len - 1; + + newes.es_lblk = lblk; + newes.es_len = len; + ext4_es_store_pblock(&newes, pblk); + ext4_es_store_status(&newes, status); + trace_ext4_es_cache_extent(inode, &newes); + + if (!len) + return; + + BUG_ON(end < lblk); + + write_lock(&EXT4_I(inode)->i_es_lock); + + es = __es_tree_search(&EXT4_I(inode)->i_es_tree.root, lblk); + if (es && ((es->es_lblk <= lblk) || (es->es_lblk <= end))) + goto out; + + __es_insert_extent(inode, &newes); +out: + write_unlock(&EXT4_I(inode)->i_es_lock); +} + /* * ext4_es_lookup_extent() looks up an extent in extent status tree. * diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h index d72af848f989..3e83aef3653a 100644 --- a/fs/ext4/extents_status.h +++ b/fs/ext4/extents_status.h @@ -71,6 +71,9 @@ extern void ext4_es_init_tree(struct ext4_es_tree *tree); extern int ext4_es_insert_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len, ext4_fsblk_t pblk, unsigned int status); +extern void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk, + ext4_lblk_t len, ext4_fsblk_t pblk, + unsigned int status); extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len); extern void ext4_es_find_delayed_extent_range(struct inode *inode, diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c index 49e8bdff9163..f99bdb8548b2 100644 --- a/fs/ext4/migrate.c +++ b/fs/ext4/migrate.c @@ -39,7 +39,7 @@ static int finish_range(handle_t *handle, struct inode *inode, newext.ee_block = cpu_to_le32(lb->first_block); newext.ee_len = cpu_to_le16(lb->last_block - lb->first_block + 1); ext4_ext_store_pblock(&newext, lb->first_pblock); - path = ext4_ext_find_extent(inode, lb->first_block, NULL); + path = ext4_ext_find_extent(inode, lb->first_block, NULL, 0); if (IS_ERR(path)) { retval = PTR_ERR(path); diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index e86dddbd8296..7fa4d855dbd5 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -37,7 +37,7 @@ get_ext_path(struct inode *inode, ext4_lblk_t lblock, int ret = 0; struct ext4_ext_path *path; - path = ext4_ext_find_extent(inode, lblock, *orig_path); + path = ext4_ext_find_extent(inode, lblock, *orig_path, EXT4_EX_NOCACHE); if (IS_ERR(path)) ret = PTR_ERR(path); else if (path[ext_depth(inode)].p_ext == NULL) diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index 47a355b251e4..d892b55d91ab 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -2192,7 +2192,7 @@ TRACE_EVENT(ext4_ext_remove_space_done, (unsigned short) __entry->eh_entries) ); -TRACE_EVENT(ext4_es_insert_extent, +DECLARE_EVENT_CLASS(ext4__es_extent, TP_PROTO(struct inode *inode, struct extent_status *es), TP_ARGS(inode, es), @@ -2222,6 +2222,18 @@ TRACE_EVENT(ext4_es_insert_extent, __entry->pblk, show_extent_status(__entry->status)) ); +DEFINE_EVENT(ext4__es_extent, ext4_es_insert_extent, + TP_PROTO(struct inode *inode, struct extent_status *es), + + TP_ARGS(inode, es) +); + +DEFINE_EVENT(ext4__es_extent, ext4_es_cache_extent, + TP_PROTO(struct inode *inode, struct extent_status *es), + + TP_ARGS(inode, es) +); + TRACE_EVENT(ext4_es_remove_extent, TP_PROTO(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t len), -- cgit v1.2.3-71-gd317 From 7869a4a6c5caa7b2e5c41ccaf46eb3371f88eea7 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 16 Aug 2013 22:05:14 -0400 Subject: ext4: add support for extent pre-caching Add a new fiemap flag which forces the all of the extents in an inode to be cached in the extent_status tree. This is critically important when using AIO to a preallocated file, since if we need to read in blocks from the extent tree, the io_submit(2) system call becomes synchronous, and the AIO is no longer "A", which is bad. In addition, for most files which have an external leaf tree block, the cost of caching the information in the extent status tree will be less than caching the entire 4k block in the buffer cache. So it is generally a win to keep the extent information cached. Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 17 ++++++----- fs/ext4/extents.c | 73 ++++++++++++++++++++++++++++++++++++++++++++- fs/ext4/extents_status.c | 72 +++++++++++++++++++++++++++++++------------- fs/ext4/ioctl.c | 3 ++ include/uapi/linux/fiemap.h | 1 + 5 files changed, 137 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index c74b1948feb0..635135e6148e 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -561,15 +561,16 @@ enum { #define EXT4_GET_BLOCKS_NO_PUT_HOLE 0x0200 /* - * The bit position of this flag must not overlap with any of the - * EXT4_GET_BLOCKS_*. It is used by ext4_ext_find_extent(), + * The bit position of these flags must not overlap with any of the + * EXT4_GET_BLOCKS_*. They are used by ext4_ext_find_extent(), * read_extent_tree_block(), ext4_split_extent_at(), - * ext4_ext_insert_extent(), and ext4_ext_create_new_leaf() to - * indicate that the we shouldn't be caching the extents when reading - * from the extent tree while a truncate or punch hole operation - * is in progress. + * ext4_ext_insert_extent(), and ext4_ext_create_new_leaf(). + * EXT4_EX_NOCACHE is used to indicate that the we shouldn't be + * caching the extents when reading from the extent tree while a + * truncate or punch hole operation is in progress. */ #define EXT4_EX_NOCACHE 0x0400 +#define EXT4_EX_FORCE_CACHE 0x0800 /* * Flags used by ext4_free_blocks @@ -601,6 +602,7 @@ enum { #define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent) #define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64) #define EXT4_IOC_SWAP_BOOT _IO('f', 17) +#define EXT4_IOC_PRECACHE_EXTENTS _IO('f', 18) #if defined(__KERNEL__) && defined(CONFIG_COMPAT) /* @@ -1386,6 +1388,7 @@ enum { nolocking */ EXT4_STATE_MAY_INLINE_DATA, /* may have in-inode data */ EXT4_STATE_ORDERED_MODE, /* data=ordered mode */ + EXT4_STATE_EXT_PRECACHED, /* extents have been precached */ }; #define EXT4_INODE_BIT_FNS(name, field, offset) \ @@ -2705,7 +2708,7 @@ extern int ext4_find_delalloc_range(struct inode *inode, extern int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk); extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len); - +extern int ext4_ext_precache(struct inode *inode); /* move_extent.c */ extern void ext4_double_down_write_data_sem(struct inode *first, diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 08c1ac976479..01838875fcaf 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -482,7 +482,7 @@ __read_extent_tree_block(const char *function, unsigned int line, if (err < 0) goto errout; } - if (buffer_verified(bh)) + if (buffer_verified(bh) && !(flags & EXT4_EX_FORCE_CACHE)) return bh; err = __ext4_ext_check(function, line, inode, ext_block_hdr(bh), depth, pblk); @@ -526,6 +526,71 @@ errout: __read_extent_tree_block(__func__, __LINE__, (inode), (pblk), \ (depth), (flags)) +/* + * This function is called to cache a file's extent information in the + * extent status tree + */ +int ext4_ext_precache(struct inode *inode) +{ + struct ext4_inode_info *ei = EXT4_I(inode); + struct ext4_ext_path *path = NULL; + struct buffer_head *bh; + int i = 0, depth, ret = 0; + + if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) + return 0; /* not an extent-mapped inode */ + + down_read(&ei->i_data_sem); + depth = ext_depth(inode); + + path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 1), + GFP_NOFS); + if (path == NULL) { + up_read(&ei->i_data_sem); + return -ENOMEM; + } + + /* Don't cache anything if there are no external extent blocks */ + if (depth == 0) + goto out; + path[0].p_hdr = ext_inode_hdr(inode); + ret = ext4_ext_check(inode, path[0].p_hdr, depth, 0); + if (ret) + goto out; + path[0].p_idx = EXT_FIRST_INDEX(path[0].p_hdr); + while (i >= 0) { + /* + * If this is a leaf block or we've reached the end of + * the index block, go up + */ + if ((i == depth) || + path[i].p_idx > EXT_LAST_INDEX(path[i].p_hdr)) { + brelse(path[i].p_bh); + path[i].p_bh = NULL; + i--; + continue; + } + bh = read_extent_tree_block(inode, + ext4_idx_pblock(path[i].p_idx++), + depth - i - 1, + EXT4_EX_FORCE_CACHE); + if (IS_ERR(bh)) { + ret = PTR_ERR(bh); + break; + } + i++; + path[i].p_bh = bh; + path[i].p_hdr = ext_block_hdr(bh); + path[i].p_idx = EXT_FIRST_INDEX(path[i].p_hdr); + } + ext4_set_inode_state(inode, EXT4_STATE_EXT_PRECACHED); +out: + up_read(&ei->i_data_sem); + ext4_ext_drop_refs(path); + kfree(path); + return ret; +} + #ifdef EXT_DEBUG static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path) { @@ -4766,6 +4831,12 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, return error; } + if (fieinfo->fi_flags & FIEMAP_FLAG_CACHE) { + error = ext4_ext_precache(inode); + if (error) + return error; + } + /* fallback to generic here if not in extents fmt */ if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) return generic_block_fiemap(inode, fieinfo, start, len, diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index 1dc5df016e25..0e88a367b535 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -710,11 +710,8 @@ void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk, write_lock(&EXT4_I(inode)->i_es_lock); es = __es_tree_search(&EXT4_I(inode)->i_es_tree.root, lblk); - if (es && ((es->es_lblk <= lblk) || (es->es_lblk <= end))) - goto out; - - __es_insert_extent(inode, &newes); -out: + if (!es || es->es_lblk > end) + __es_insert_extent(inode, &newes); write_unlock(&EXT4_I(inode)->i_es_lock); } @@ -930,6 +927,12 @@ static int ext4_inode_touch_time_cmp(void *priv, struct list_head *a, eia = list_entry(a, struct ext4_inode_info, i_es_lru); eib = list_entry(b, struct ext4_inode_info, i_es_lru); + if (ext4_test_inode_state(&eia->vfs_inode, EXT4_STATE_EXT_PRECACHED) && + !ext4_test_inode_state(&eib->vfs_inode, EXT4_STATE_EXT_PRECACHED)) + return 1; + if (!ext4_test_inode_state(&eia->vfs_inode, EXT4_STATE_EXT_PRECACHED) && + ext4_test_inode_state(&eib->vfs_inode, EXT4_STATE_EXT_PRECACHED)) + return -1; if (eia->i_touch_when == eib->i_touch_when) return 0; if (time_after(eia->i_touch_when, eib->i_touch_when)) @@ -943,21 +946,13 @@ static int __ext4_es_shrink(struct ext4_sb_info *sbi, int nr_to_scan, { struct ext4_inode_info *ei; struct list_head *cur, *tmp; - LIST_HEAD(skiped); + LIST_HEAD(skipped); int ret, nr_shrunk = 0; + int retried = 0, skip_precached = 1, nr_skipped = 0; spin_lock(&sbi->s_es_lru_lock); - /* - * If the inode that is at the head of LRU list is newer than - * last_sorted time, that means that we need to sort this list. - */ - ei = list_first_entry(&sbi->s_es_lru, struct ext4_inode_info, i_es_lru); - if (sbi->s_es_last_sorted < ei->i_touch_when) { - list_sort(NULL, &sbi->s_es_lru, ext4_inode_touch_time_cmp); - sbi->s_es_last_sorted = jiffies; - } - +retry: list_for_each_safe(cur, tmp, &sbi->s_es_lru) { /* * If we have already reclaimed all extents from extent @@ -968,9 +963,16 @@ static int __ext4_es_shrink(struct ext4_sb_info *sbi, int nr_to_scan, ei = list_entry(cur, struct ext4_inode_info, i_es_lru); - /* Skip the inode that is newer than the last_sorted time */ - if (sbi->s_es_last_sorted < ei->i_touch_when) { - list_move_tail(cur, &skiped); + /* + * Skip the inode that is newer than the last_sorted + * time. Normally we try hard to avoid shrinking + * precached inodes, but we will as a last resort. + */ + if ((sbi->s_es_last_sorted < ei->i_touch_when) || + (skip_precached && ext4_test_inode_state(&ei->vfs_inode, + EXT4_STATE_EXT_PRECACHED))) { + nr_skipped++; + list_move_tail(cur, &skipped); continue; } @@ -990,11 +992,33 @@ static int __ext4_es_shrink(struct ext4_sb_info *sbi, int nr_to_scan, } /* Move the newer inodes into the tail of the LRU list. */ - list_splice_tail(&skiped, &sbi->s_es_lru); + list_splice_tail(&skipped, &sbi->s_es_lru); + INIT_LIST_HEAD(&skipped); + + /* + * If we skipped any inodes, and we weren't able to make any + * forward progress, sort the list and try again. + */ + if ((nr_shrunk == 0) && nr_skipped && !retried) { + retried++; + list_sort(NULL, &sbi->s_es_lru, ext4_inode_touch_time_cmp); + sbi->s_es_last_sorted = jiffies; + ei = list_first_entry(&sbi->s_es_lru, struct ext4_inode_info, + i_es_lru); + /* + * If there are no non-precached inodes left on the + * list, start releasing precached extents. + */ + if (ext4_test_inode_state(&ei->vfs_inode, + EXT4_STATE_EXT_PRECACHED)) + skip_precached = 0; + goto retry; + } + spin_unlock(&sbi->s_es_lru_lock); if (locked_ei && nr_shrunk == 0) - nr_shrunk = __es_try_to_reclaim_extents(ei, nr_to_scan); + nr_shrunk = __es_try_to_reclaim_extents(locked_ei, nr_to_scan); return nr_shrunk; } @@ -1069,10 +1093,16 @@ static int __es_try_to_reclaim_extents(struct ext4_inode_info *ei, struct rb_node *node; struct extent_status *es; int nr_shrunk = 0; + static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); if (ei->i_es_lru_nr == 0) return 0; + if (ext4_test_inode_state(inode, EXT4_STATE_EXT_PRECACHED) && + __ratelimit(&_rs)) + ext4_warning(inode->i_sb, "forced shrink of precached extents"); + node = rb_first(&tree->root); while (node != NULL) { es = rb_entry(node, struct extent_status, rb_node); diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index c0427e2f6648..5498f75a1648 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -624,6 +624,8 @@ resizefs_out: return 0; } + case EXT4_IOC_PRECACHE_EXTENTS: + return ext4_ext_precache(inode); default: return -ENOTTY; @@ -688,6 +690,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case EXT4_IOC_MOVE_EXT: case FITRIM: case EXT4_IOC_RESIZE_FS: + case EXT4_IOC_PRECACHE_EXTENTS: break; default: return -ENOIOCTLCMD; diff --git a/include/uapi/linux/fiemap.h b/include/uapi/linux/fiemap.h index d830747f5c0b..0c51d617dae9 100644 --- a/include/uapi/linux/fiemap.h +++ b/include/uapi/linux/fiemap.h @@ -40,6 +40,7 @@ struct fiemap { #define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */ #define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */ +#define FIEMAP_FLAG_CACHE 0x00000004 /* request caching of the extents */ #define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR) -- cgit v1.2.3-71-gd317 From 1c8924eb106c1ac755d5d35ce9b3ff42e89e2511 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Sat, 17 Aug 2013 09:32:32 -0400 Subject: quota: provide interface for readding allocated space into reserved space ext4 needs to convert allocated (metadata) blocks back into blocks reserved for delayed allocation. Add functions into quota code for supporting such operation. Signed-off-by: Jan Kara Signed-off-by: "Theodore Ts'o" --- fs/quota/dquot.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ fs/stat.c | 11 +++++++++-- include/linux/fs.h | 1 + include/linux/quotaops.h | 15 +++++++++++++++ 4 files changed, 71 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index fbad622841f9..9a702e193538 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -1094,6 +1094,14 @@ static void dquot_claim_reserved_space(struct dquot *dquot, qsize_t number) dquot->dq_dqb.dqb_rsvspace -= number; } +static void dquot_reclaim_reserved_space(struct dquot *dquot, qsize_t number) +{ + if (WARN_ON_ONCE(dquot->dq_dqb.dqb_curspace < number)) + number = dquot->dq_dqb.dqb_curspace; + dquot->dq_dqb.dqb_rsvspace += number; + dquot->dq_dqb.dqb_curspace -= number; +} + static inline void dquot_free_reserved_space(struct dquot *dquot, qsize_t number) { @@ -1528,6 +1536,15 @@ void inode_claim_rsv_space(struct inode *inode, qsize_t number) } EXPORT_SYMBOL(inode_claim_rsv_space); +void inode_reclaim_rsv_space(struct inode *inode, qsize_t number) +{ + spin_lock(&inode->i_lock); + *inode_reserved_space(inode) += number; + __inode_sub_bytes(inode, number); + spin_unlock(&inode->i_lock); +} +EXPORT_SYMBOL(inode_reclaim_rsv_space); + void inode_sub_rsv_space(struct inode *inode, qsize_t number) { spin_lock(&inode->i_lock); @@ -1701,6 +1718,35 @@ int dquot_claim_space_nodirty(struct inode *inode, qsize_t number) } EXPORT_SYMBOL(dquot_claim_space_nodirty); +/* + * Convert allocated space back to in-memory reserved quotas + */ +void dquot_reclaim_space_nodirty(struct inode *inode, qsize_t number) +{ + int cnt; + + if (!dquot_active(inode)) { + inode_reclaim_rsv_space(inode, number); + return; + } + + down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + spin_lock(&dq_data_lock); + /* Claim reserved quotas to allocated quotas */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + if (inode->i_dquot[cnt]) + dquot_reclaim_reserved_space(inode->i_dquot[cnt], + number); + } + /* Update inode bytes */ + inode_reclaim_rsv_space(inode, number); + spin_unlock(&dq_data_lock); + mark_all_dquot_dirty(inode->i_dquot); + up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + return; +} +EXPORT_SYMBOL(dquot_reclaim_space_nodirty); + /* * This operation can block, but only after everything is updated */ diff --git a/fs/stat.c b/fs/stat.c index 04ce1ac20d20..d0ea7ef75e26 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -447,9 +447,8 @@ void inode_add_bytes(struct inode *inode, loff_t bytes) EXPORT_SYMBOL(inode_add_bytes); -void inode_sub_bytes(struct inode *inode, loff_t bytes) +void __inode_sub_bytes(struct inode *inode, loff_t bytes) { - spin_lock(&inode->i_lock); inode->i_blocks -= bytes >> 9; bytes &= 511; if (inode->i_bytes < bytes) { @@ -457,6 +456,14 @@ void inode_sub_bytes(struct inode *inode, loff_t bytes) inode->i_bytes += 512; } inode->i_bytes -= bytes; +} + +EXPORT_SYMBOL(__inode_sub_bytes); + +void inode_sub_bytes(struct inode *inode, loff_t bytes) +{ + spin_lock(&inode->i_lock); + __inode_sub_bytes(inode, bytes); spin_unlock(&inode->i_lock); } diff --git a/include/linux/fs.h b/include/linux/fs.h index 981874773e85..e7893523f81f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2503,6 +2503,7 @@ extern void generic_fillattr(struct inode *, struct kstat *); extern int vfs_getattr(struct path *, struct kstat *); void __inode_add_bytes(struct inode *inode, loff_t bytes); void inode_add_bytes(struct inode *inode, loff_t bytes); +void __inode_sub_bytes(struct inode *inode, loff_t bytes); void inode_sub_bytes(struct inode *inode, loff_t bytes); loff_t inode_get_bytes(struct inode *inode); void inode_set_bytes(struct inode *inode, loff_t bytes); diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 1c50093ae656..6965fe394c3b 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -41,6 +41,7 @@ void __quota_error(struct super_block *sb, const char *func, void inode_add_rsv_space(struct inode *inode, qsize_t number); void inode_claim_rsv_space(struct inode *inode, qsize_t number); void inode_sub_rsv_space(struct inode *inode, qsize_t number); +void inode_reclaim_rsv_space(struct inode *inode, qsize_t number); void dquot_initialize(struct inode *inode); void dquot_drop(struct inode *inode); @@ -59,6 +60,7 @@ int dquot_alloc_inode(const struct inode *inode); int dquot_claim_space_nodirty(struct inode *inode, qsize_t number); void dquot_free_inode(const struct inode *inode); +void dquot_reclaim_space_nodirty(struct inode *inode, qsize_t number); int dquot_disable(struct super_block *sb, int type, unsigned int flags); /* Suspend quotas on remount RO */ @@ -238,6 +240,13 @@ static inline int dquot_claim_space_nodirty(struct inode *inode, qsize_t number) return 0; } +static inline int dquot_reclaim_space_nodirty(struct inode *inode, + qsize_t number) +{ + inode_sub_bytes(inode, number); + return 0; +} + static inline int dquot_disable(struct super_block *sb, int type, unsigned int flags) { @@ -336,6 +345,12 @@ static inline int dquot_claim_block(struct inode *inode, qsize_t nr) return ret; } +static inline void dquot_reclaim_block(struct inode *inode, qsize_t nr) +{ + dquot_reclaim_space_nodirty(inode, nr << inode->i_blkbits); + mark_inode_dirty_sync(inode); +} + static inline void dquot_free_space_nodirty(struct inode *inode, qsize_t nr) { __dquot_free_space(inode, nr, 0); -- cgit v1.2.3-71-gd317 From d536321df392ee0db6a5e92d98f0303020315f40 Mon Sep 17 00:00:00 2001 From: Jacek Anaszewski Date: Fri, 16 Aug 2013 14:11:00 +0100 Subject: iio: trigger: implement devm_iio_trigger_alloc/devm_iio_triger_free Add a resource managed devm_iio_trigger_alloc()/devm_iio_triger_free() to automatically clean up triggers allocated by IIO drivers, thus leading to simplified IIO drivers code. Signed-off-by: Jacek Anaszewski Signed-off-by: Kyunmin Park Signed-off-by: Jonathan Cameron --- Documentation/driver-model/devres.txt | 2 + drivers/iio/industrialio-trigger.c | 72 +++++++++++++++++++++++++++++++++-- include/linux/iio/iio.h | 29 ++++++++++++++ 3 files changed, 99 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 1d3233292989..fb57d85e7316 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -240,6 +240,8 @@ MEM IIO devm_iio_device_alloc() devm_iio_device_free() + devm_iio_trigger_alloc() + devm_iio_trigger_free() IO region devm_request_region() diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 0dd9bb873130..bf5e70a32d3f 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -424,9 +424,8 @@ static void iio_trig_subirqunmask(struct irq_data *d) trig->subirqs[d->irq - trig->subirq_base].enabled = true; } -struct iio_trigger *iio_trigger_alloc(const char *fmt, ...) +static struct iio_trigger *viio_trigger_alloc(const char *fmt, va_list vargs) { - va_list vargs; struct iio_trigger *trig; trig = kzalloc(sizeof *trig, GFP_KERNEL); if (trig) { @@ -444,9 +443,8 @@ struct iio_trigger *iio_trigger_alloc(const char *fmt, ...) kfree(trig); return NULL; } - va_start(vargs, fmt); + trig->name = kvasprintf(GFP_KERNEL, fmt, vargs); - va_end(vargs); if (trig->name == NULL) { irq_free_descs(trig->subirq_base, CONFIG_IIO_CONSUMERS_PER_TRIGGER); @@ -467,6 +465,19 @@ struct iio_trigger *iio_trigger_alloc(const char *fmt, ...) } get_device(&trig->dev); } + + return trig; +} + +struct iio_trigger *iio_trigger_alloc(const char *fmt, ...) +{ + struct iio_trigger *trig; + va_list vargs; + + va_start(vargs, fmt); + trig = viio_trigger_alloc(fmt, vargs); + va_end(vargs); + return trig; } EXPORT_SYMBOL(iio_trigger_alloc); @@ -478,6 +489,59 @@ void iio_trigger_free(struct iio_trigger *trig) } EXPORT_SYMBOL(iio_trigger_free); +static void devm_iio_trigger_release(struct device *dev, void *res) +{ + iio_trigger_free(*(struct iio_trigger **)res); +} + +static int devm_iio_trigger_match(struct device *dev, void *res, void *data) +{ + struct iio_trigger **r = res; + + if (!r || !*r) { + WARN_ON(!r || !*r); + return 0; + } + + return *r == data; +} + +struct iio_trigger *devm_iio_trigger_alloc(struct device *dev, + const char *fmt, ...) +{ + struct iio_trigger **ptr, *trig; + va_list vargs; + + ptr = devres_alloc(devm_iio_trigger_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return NULL; + + /* use raw alloc_dr for kmalloc caller tracing */ + va_start(vargs, fmt); + trig = viio_trigger_alloc(fmt, vargs); + va_end(vargs); + if (trig) { + *ptr = trig; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return trig; +} +EXPORT_SYMBOL_GPL(devm_iio_trigger_alloc); + +void devm_iio_trigger_free(struct device *dev, struct iio_trigger *iio_trig) +{ + int rc; + + rc = devres_release(dev, devm_iio_trigger_release, + devm_iio_trigger_match, iio_trig); + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_iio_trigger_free); + void iio_device_register_trigger_consumer(struct iio_dev *indio_dev) { indio_dev->groups[indio_dev->groupcounter++] = diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 09ebe0a4d8e6..2103cc32a5fb 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -556,6 +556,35 @@ struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv); */ void devm_iio_device_free(struct device *dev, struct iio_dev *indio_dev); +/** + * devm_iio_trigger_alloc - Resource-managed iio_trigger_alloc() + * @dev: Device to allocate iio_trigger for + * @fmt: trigger name format. If it includes format + * specifiers, the additional arguments following + * format are formatted and inserted in the resulting + * string replacing their respective specifiers. + * + * Managed iio_trigger_alloc. iio_trigger allocated with this function is + * automatically freed on driver detach. + * + * If an iio_trigger allocated with this function needs to be freed separately, + * devm_iio_trigger_free() must be used. + * + * RETURNS: + * Pointer to allocated iio_trigger on success, NULL on failure. + */ +struct iio_trigger *devm_iio_trigger_alloc(struct device *dev, + const char *fmt, ...); + +/** + * devm_iio_trigger_free - Resource-managed iio_trigger_free() + * @dev: Device this iio_dev belongs to + * @iio_trig: the iio_trigger associated with the device + * + * Free iio_trigger allocated with devm_iio_trigger_alloc(). + */ +void devm_iio_trigger_free(struct device *dev, struct iio_trigger *iio_trig); + /** * iio_buffer_enabled() - helper function to test if the buffer is enabled * @indio_dev: IIO device structure for device -- cgit v1.2.3-71-gd317 From bc9028e1d38419f9249cb0d1285e290be7e67223 Mon Sep 17 00:00:00 2001 From: Arun Kumar K Date: Tue, 9 Jul 2013 01:24:41 -0300 Subject: [media] V4L: Add VP8 encoder controls This patch adds new V4L controls for VP8 encoding. Signed-off-by: Kiran AVND Signed-off-by: Arun Kumar K Acked-by: Hans Verkuil Signed-off-by: Kamil Debski Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 168 ++++++++++++++++++++++++++- drivers/media/v4l2-core/v4l2-ctrls.c | 39 ++++++- include/uapi/linux/v4l2-controls.h | 29 +++++ 3 files changed, 229 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index c2fc9ec1417e..7a3b49b3cc3b 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -722,17 +722,22 @@ for more details.
- MPEG Control Reference + Codec Control Reference - Below all controls within the MPEG control class are + Below all controls within the Codec control class are described. First the generic controls, then controls specific for certain hardware. + Note: These controls are applicable to all codecs and +not just MPEG. The defines are prefixed with V4L2_CID_MPEG/V4L2_MPEG +as the controls were originally made for MPEG codecs and later +extended to cover all encoding formats. +
- Generic MPEG Controls + Generic Codec Controls - MPEG Control IDs + Codec Control IDs @@ -752,7 +757,7 @@ certain hardware. V4L2_CID_MPEG_CLASS  class - The MPEG class + The Codec class descriptor. Calling &VIDIOC-QUERYCTRL; for this control will return a description of this control class. This description can be used as the caption of a Tab page in a GUI, for example. @@ -3009,6 +3014,159 @@ in by the application. 0 = do not insert, 1 = insert packets.
+ +
+ VPX Control Reference + + The VPX controls include controls for encoding parameters + of VPx video codec. + + + VPX Control IDs + + + + + + + + + + + ID + Type + Description + + + + + + + + V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS + enum v4l2_vp8_num_partitions + + The number of token partitions to use in VP8 encoder. +Possible values are: + + + + + + V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION + 1 coefficient partition + + + V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS + 2 coefficient partitions + + + V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS + 4 coefficient partitions + + + V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS + 8 coefficient partitions + + + + + + + + V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4 + boolean + + Setting this prevents intra 4x4 mode in the intra mode decision. + + + + + V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES + enum v4l2_vp8_num_ref_frames + + The number of reference pictures for encoding P frames. +Possible values are: + + + + + + V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME + Last encoded frame will be searched + + + V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME + Two frames will be searched among the last encoded frame, the golden frame +and the alternate reference (altref) frame. The encoder implementation will decide which two are chosen. + + + V4L2_CID_MPEG_VIDEO_VPX_3_REF_FRAME + The last encoded frame, the golden frame and the altref frame will be searched. + + + + + + + + V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL + integer + + Indicates the loop filter level. The adjustment of the loop +filter level is done via a delta value against a baseline loop filter value. + + + + + V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS + integer + + This parameter affects the loop filter. Anything above +zero weakens the deblocking effect on the loop filter. + + + + + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD + integer + + Sets the refresh period for the golden frame. The period is defined +in number of frames. For a value of 'n', every nth frame starting from the first key frame will be taken as a golden frame. +For eg. for encoding sequence of 0, 1, 2, 3, 4, 5, 6, 7 where the golden frame refresh period is set as 4, the frames +0, 4, 8 etc will be taken as the golden frames as frame 0 is always a key frame. + + + + + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL + enum v4l2_vp8_golden_frame_sel + + Selects the golden frame for encoding. +Possible values are: + + + + + + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV + Use the (n-2)th frame as a golden frame, current frame index being 'n'. + + + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD + Use the previous specific frame indicated by +V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD as a golden frame. + + + + + + + + +
+ +
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e03a2e852143..c6dc1fd427d7 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -424,6 +424,12 @@ const char * const *v4l2_ctrl_get_menu(u32 id) NULL, }; + static const char * const vpx_golden_frame_sel[] = { + "Use Previous Frame", + "Use Previous Specific Frame", + NULL, + }; + static const char * const flash_led_mode[] = { "Off", "Flash", @@ -538,6 +544,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return mpeg_mpeg4_level; case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: return mpeg4_profile; + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: + return vpx_golden_frame_sel; case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return jpeg_chroma_subsampling; case V4L2_CID_DV_TX_MODE: @@ -552,13 +560,26 @@ const char * const *v4l2_ctrl_get_menu(u32 id) } EXPORT_SYMBOL(v4l2_ctrl_get_menu); +#define __v4l2_qmenu_int_len(arr, len) ({ *(len) = ARRAY_SIZE(arr); arr; }) /* * Returns NULL or an s64 type array containing the menu for given * control ID. The total number of the menu items is returned in @len. */ const s64 const *v4l2_ctrl_get_int_menu(u32 id, u32 *len) { + static const s64 const qmenu_int_vpx_num_partitions[] = { + 1, 2, 4, 8, + }; + + static const s64 const qmenu_int_vpx_num_ref_frames[] = { + 1, 2, 3, + }; + switch (id) { + case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS: + return __v4l2_qmenu_int_len(qmenu_int_vpx_num_partitions, len); + case V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES: + return __v4l2_qmenu_int_len(qmenu_int_vpx_num_ref_frames, len); default: *len = 0; return NULL; @@ -614,9 +635,11 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_ALPHA_COMPONENT: return "Alpha Component"; case V4L2_CID_COLORFX_CBCR: return "Color Effects, CbCr"; - /* MPEG controls */ + /* Codec controls */ + /* The MPEG controls are applicable to all codec controls + * and the 'MPEG' part of the define is historical */ /* Keep the order of the 'case's the same as in videodev2.h! */ - case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; + case V4L2_CID_MPEG_CLASS: return "Codec Controls"; case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type"; case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID"; case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID"; @@ -714,6 +737,15 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VBV_DELAY: return "Initial Delay for VBV Control"; case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER: return "Repeat Sequence Header"; + /* VPX controls */ + case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS: return "VPX Number of Partitions"; + case V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4: return "VPX Intra Mode Decision Disable"; + case V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES: return "VPX No. of Refs for P Frame"; + case V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL: return "VPX Loop Filter Level Range"; + case V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS: return "VPX Deblocking Effect Control"; + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD: return "VPX Golden Frame Refresh Period"; + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: return "VPX Golden Frame Indicator"; + /* CAMERA controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ case V4L2_CID_CAMERA_CLASS: return "Camera Controls"; @@ -928,6 +960,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DV_RX_RGB_RANGE: case V4L2_CID_TEST_PATTERN: case V4L2_CID_TUNE_DEEMPHASIS: + case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: @@ -939,6 +972,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, break; case V4L2_CID_ISO_SENSITIVITY: case V4L2_CID_AUTO_EXPOSURE_BIAS: + case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS: + case V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES: *type = V4L2_CTRL_TYPE_INTEGER_MENU; break; case V4L2_CID_USER_CLASS: diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index e90a88a8708f..083bb5a5aae2 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -161,6 +161,8 @@ enum v4l2_colorfx { #define V4L2_CID_USER_SI476X_BASE (V4L2_CID_USER_BASE + 0x1040) /* MPEG-class control IDs */ +/* The MPEG controls are applicable to all codec controls + * and the 'MPEG' part of the define is historical */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) #define V4L2_CID_MPEG_CLASS (V4L2_CTRL_CLASS_MPEG | 1) @@ -522,6 +524,33 @@ enum v4l2_mpeg_video_mpeg4_profile { }; #define V4L2_CID_MPEG_VIDEO_MPEG4_QPEL (V4L2_CID_MPEG_BASE+407) +/* Control IDs for VP8 streams + * Although VP8 is not part of MPEG we add these controls to the MPEG class + * as that class is already handling other video compression standards + */ +#define V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS (V4L2_CID_MPEG_BASE+500) +enum v4l2_vp8_num_partitions { + V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION = 0, + V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS = 1, + V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS = 2, + V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS = 3, +}; +#define V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4 (V4L2_CID_MPEG_BASE+501) +#define V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES (V4L2_CID_MPEG_BASE+502) +enum v4l2_vp8_num_ref_frames { + V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME = 0, + V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME = 1, + V4L2_CID_MPEG_VIDEO_VPX_3_REF_FRAME = 2, +}; +#define V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL (V4L2_CID_MPEG_BASE+503) +#define V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS (V4L2_CID_MPEG_BASE+504) +#define V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD (V4L2_CID_MPEG_BASE+505) +#define V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL (V4L2_CID_MPEG_BASE+506) +enum v4l2_vp8_golden_frame_sel { + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV = 0, + V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD = 1, +}; + /* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+0) -- cgit v1.2.3-71-gd317 From 5c7b25b90d36942c524d06522ebaf0510a75592a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 7 Jun 2013 12:45:11 -0300 Subject: [media] media: Add support for circular graph traversal The graph traversal API (media_entity_graph_walk_*) doesn't support cyclic graphs and will fail to correctly walk a graph when circular links exist. Support circular graph traversal by checking whether an entity has already been visited before pushing it to the stack. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-entity.c | 14 +++++++++++--- include/media/media-entity.h | 4 ++++ 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index cb30ffbd5ba8..2c286c307145 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -20,6 +20,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include @@ -121,7 +122,6 @@ static struct media_entity *stack_pop(struct media_entity_graph *graph) return entity; } -#define stack_peek(en) ((en)->stack[(en)->top - 1].entity) #define link_top(en) ((en)->stack[(en)->top].link) #define stack_top(en) ((en)->stack[(en)->top].entity) @@ -140,6 +140,12 @@ void media_entity_graph_walk_start(struct media_entity_graph *graph, { graph->top = 0; graph->stack[graph->top].entity = NULL; + bitmap_zero(graph->entities, MEDIA_ENTITY_ENUM_MAX_ID); + + if (WARN_ON(entity->id >= MEDIA_ENTITY_ENUM_MAX_ID)) + return; + + __set_bit(entity->id, graph->entities); stack_push(graph, entity); } EXPORT_SYMBOL_GPL(media_entity_graph_walk_start); @@ -180,9 +186,11 @@ media_entity_graph_walk_next(struct media_entity_graph *graph) /* Get the entity in the other end of the link . */ next = media_entity_other(entity, link); + if (WARN_ON(next->id >= MEDIA_ENTITY_ENUM_MAX_ID)) + return NULL; - /* Was it the entity we came here from? */ - if (next == stack_peek(graph)) { + /* Has the entity already been visited? */ + if (__test_and_set_bit(next->id, graph->entities)) { link_top(graph)++; continue; } diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 06bacf937d61..10df55187981 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -23,6 +23,7 @@ #ifndef _MEDIA_ENTITY_H #define _MEDIA_ENTITY_H +#include #include #include @@ -113,12 +114,15 @@ static inline u32 media_entity_subtype(struct media_entity *entity) } #define MEDIA_ENTITY_ENUM_MAX_DEPTH 16 +#define MEDIA_ENTITY_ENUM_MAX_ID 64 struct media_entity_graph { struct { struct media_entity *entity; int link; } stack[MEDIA_ENTITY_ENUM_MAX_DEPTH]; + + DECLARE_BITMAP(entities, MEDIA_ENTITY_ENUM_MAX_ID); int top; }; -- cgit v1.2.3-71-gd317 From 5548a382508f1f76320e1ed55524dda73234299d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 1 Aug 2013 21:44:38 -0300 Subject: [media] media: vb2: Clarify queue_setup() and buf_prepare() usage documentation Explain how the two operations must handle formats and validate buffer sizes when used with VIDIOC_CREATE_BUFS. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/videobuf2-core.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index d88a098d1aff..6781258d0b67 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -219,8 +219,9 @@ struct vb2_buffer { * configured format and *num_buffers is the total number * of buffers, that are being allocated. When called from * VIDIOC_CREATE_BUFS, fmt != NULL and it describes the - * target frame format. In this case *num_buffers are being - * allocated additionally to q->num_buffers. + * target frame format (if the format isn't valid the + * callback must return -EINVAL). In this case *num_buffers + * are being allocated additionally to q->num_buffers. * @wait_prepare: release any locks taken while calling vb2 functions; * it is called before an ioctl needs to wait for a new * buffer to arrive; required to avoid a deadlock in @@ -236,8 +237,10 @@ struct vb2_buffer { * @buf_prepare: called every time the buffer is queued from userspace * and from the VIDIOC_PREPARE_BUF ioctl; drivers may * perform any initialization required before each hardware - * operation in this callback; if an error is returned, the - * buffer will not be queued in driver; optional + * operation in this callback; drivers that support + * VIDIOC_CREATE_BUFS must also validate the buffer size; + * if an error is returned, the buffer will not be queued + * in driver; optional * @buf_finish: called before every dequeue of the buffer back to * userspace; drivers may perform any operations required * before userspace accesses the buffer; optional -- cgit v1.2.3-71-gd317 From f57fa2102cd5c0b50359def79a3d39cda8431204 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 5 Jun 2013 04:19:53 -0300 Subject: [media] v4l: Add media format codes for ARGB8888 and AYUV8888 on 32-bit busses Signed-off-by: Laurent Pinchart Reviewed-by: Sylwester Nawrocki Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/subdev-formats.xml | 609 +++++++++------------ Documentation/DocBook/media_api.tmpl | 6 + include/uapi/linux/v4l2-mediabus.h | 6 +- 3 files changed, 254 insertions(+), 367 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/subdev-formats.xml b/Documentation/DocBook/media/v4l/subdev-formats.xml index 0c2b1f2e8754..f72c1cc93a9b 100644 --- a/Documentation/DocBook/media/v4l/subdev-formats.xml +++ b/Documentation/DocBook/media/v4l/subdev-formats.xml @@ -97,31 +97,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Identifier @@ -133,6 +141,14 @@ Bit + 31 + 30 + 29 + 28 + 27 + 26 + 25 + 24 23 22 21 @@ -164,7 +180,7 @@ V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE 0x1001 - &dash-ent-16; + &dash-ent-24; 0 0 0 @@ -178,7 +194,7 @@ - &dash-ent-16; + &dash-ent-24; g3 g2 g1 @@ -192,7 +208,7 @@ V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE 0x1002 - &dash-ent-16; + &dash-ent-24; g3 g2 g1 @@ -206,7 +222,7 @@ - &dash-ent-16; + &dash-ent-24; 0 0 0 @@ -220,7 +236,7 @@ V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE 0x1003 - &dash-ent-16; + &dash-ent-24; 0 r4 r3 @@ -234,7 +250,7 @@ - &dash-ent-16; + &dash-ent-24; g2 g1 g0 @@ -248,7 +264,7 @@ V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE 0x1004 - &dash-ent-16; + &dash-ent-24; g2 g1 g0 @@ -262,7 +278,7 @@ - &dash-ent-16; + &dash-ent-24; 0 r4 r3 @@ -276,7 +292,7 @@ V4L2_MBUS_FMT_BGR565_2X8_BE 0x1005 - &dash-ent-16; + &dash-ent-24; b4 b3 b2 @@ -290,7 +306,7 @@ - &dash-ent-16; + &dash-ent-24; g2 g1 g0 @@ -304,7 +320,7 @@ V4L2_MBUS_FMT_BGR565_2X8_LE 0x1006 - &dash-ent-16; + &dash-ent-24; g2 g1 g0 @@ -318,7 +334,7 @@ - &dash-ent-16; + &dash-ent-24; b4 b3 b2 @@ -332,7 +348,7 @@ V4L2_MBUS_FMT_RGB565_2X8_BE 0x1007 - &dash-ent-16; + &dash-ent-24; r4 r3 r2 @@ -346,7 +362,7 @@ - &dash-ent-16; + &dash-ent-24; g2 g1 g0 @@ -360,7 +376,7 @@ V4L2_MBUS_FMT_RGB565_2X8_LE 0x1008 - &dash-ent-16; + &dash-ent-24; g2 g1 g0 @@ -374,7 +390,7 @@ - &dash-ent-16; + &dash-ent-24; r4 r3 r2 @@ -388,12 +404,7 @@ V4L2_MBUS_FMT_RGB666_1X18 0x1009 - - - - - - - - - - - - + &dash-ent-14; r5 r4 r3 @@ -417,6 +428,7 @@ V4L2_MBUS_FMT_RGB888_1X24 0x100a + &dash-ent-8; r7 r6 r5 @@ -446,9 +458,7 @@ V4L2_MBUS_FMT_RGB888_2X12_BE 0x100b - &dash-ent-10; - - - - + &dash-ent-20; r7 r6 r5 @@ -466,9 +476,7 @@ - &dash-ent-10; - - - - + &dash-ent-20; g3 g2 g1 @@ -486,9 +494,7 @@ V4L2_MBUS_FMT_RGB888_2X12_LE 0x100c - &dash-ent-10; - - - - + &dash-ent-20; g3 g2 g1 @@ -506,9 +512,7 @@ - &dash-ent-10; - - - - + &dash-ent-20; r7 r6 r5 @@ -522,6 +526,43 @@ g5 g4 + + V4L2_MBUS_FMT_ARGB888_1X32 + 0x100d + + a7 + a6 + a5 + a4 + a3 + a2 + a1 + a0 + r7 + r6 + r5 + r4 + r3 + r2 + r1 + r0 + g7 + g6 + g5 + g4 + g3 + g2 + g1 + g0 + b7 + b6 + b5 + b4 + b3 + b2 + b1 + b0 + @@ -1149,6 +1190,7 @@ yx for luma component bit number x ux for blue chroma component bit number x vx for red chroma component bit number x + ax for alpha component bit number x - for non-available bits (for positions higher than the bus width) d for dummy bits @@ -1159,37 +1201,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Identifier @@ -1201,6 +1245,8 @@ Bit + 31 + 30 29 28 27 @@ -1238,10 +1284,7 @@ V4L2_MBUS_FMT_Y8_1X8 0x2001 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1255,18 +1298,7 @@ V4L2_MBUS_FMT_UV8_1X8 0x2015 - - - - - - - - - - - - - - - - - - - - - - - - + &dash-ent-24; u7 u6 u5 @@ -1280,18 +1312,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - + &dash-ent-24; v7 v6 v5 @@ -1305,10 +1326,7 @@ V4L2_MBUS_FMT_UYVY8_1_5X8 0x2002 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1322,10 +1340,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1339,10 +1354,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1356,10 +1368,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1373,10 +1382,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1390,10 +1396,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1407,10 +1410,7 @@ V4L2_MBUS_FMT_VYUY8_1_5X8 0x2003 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1424,10 +1424,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1441,10 +1438,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1458,10 +1452,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1475,10 +1466,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1492,10 +1480,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1509,10 +1494,7 @@ V4L2_MBUS_FMT_YUYV8_1_5X8 0x2004 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1526,10 +1508,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1543,10 +1522,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1560,10 +1536,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1577,10 +1550,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1594,10 +1564,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1611,10 +1578,7 @@ V4L2_MBUS_FMT_YVYU8_1_5X8 0x2005 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1628,10 +1592,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1645,10 +1606,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1662,10 +1620,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1679,10 +1634,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1696,10 +1648,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1713,10 +1662,7 @@ V4L2_MBUS_FMT_UYVY8_2X8 0x2006 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1730,10 +1676,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1747,10 +1690,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1764,10 +1704,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1781,10 +1718,7 @@ V4L2_MBUS_FMT_VYUY8_2X8 0x2007 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1798,10 +1732,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1815,10 +1746,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1832,10 +1760,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1849,10 +1774,7 @@ V4L2_MBUS_FMT_YUYV8_2X8 0x2008 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1866,10 +1788,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1883,10 +1802,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1900,10 +1816,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1917,10 +1830,7 @@ V4L2_MBUS_FMT_YVYU8_2X8 0x2009 - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1934,10 +1844,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; v7 v6 v5 @@ -1951,10 +1858,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; y7 y6 y5 @@ -1968,10 +1872,7 @@ - &dash-ent-10; - &dash-ent-10; - - - - + &dash-ent-24; u7 u6 u5 @@ -1985,8 +1886,7 @@ V4L2_MBUS_FMT_Y10_1X10 0x200a - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; y9 y8 y7 @@ -2002,8 +1902,7 @@ V4L2_MBUS_FMT_YUYV10_2X10 0x200b - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; y9 y8 y7 @@ -2019,8 +1918,7 @@ - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; u9 u8 u7 @@ -2036,8 +1934,7 @@ - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; y9 y8 y7 @@ -2053,8 +1950,7 @@ - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; v9 v8 v7 @@ -2070,8 +1966,7 @@ V4L2_MBUS_FMT_YVYU10_2X10 0x200c - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; y9 y8 y7 @@ -2087,8 +1982,7 @@ - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; v9 v8 v7 @@ -2104,8 +1998,7 @@ - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; y9 y8 y7 @@ -2121,8 +2014,7 @@ - &dash-ent-10; - &dash-ent-10; + &dash-ent-22; u9 u8 u7 @@ -2138,15 +2030,7 @@ V4L2_MBUS_FMT_Y12_1X12 0x2013 - &dash-ent-10; - - - - - - - - - - - - - - - - + &dash-ent-20; y11 y10 y9 @@ -2164,11 +2048,7 @@ V4L2_MBUS_FMT_UYVY8_1X16 0x200f - &dash-ent-10; - - - - - - - - + &dash-ent-16; u7 u6 u5 @@ -2190,11 +2070,7 @@ - &dash-ent-10; - - - - - - - - + &dash-ent-16; v7 v6 v5 @@ -2216,11 +2092,7 @@ V4L2_MBUS_FMT_VYUY8_1X16 0x2010 - &dash-ent-10; - - - - - - - - + &dash-ent-16; v7 v6 v5 @@ -2242,11 +2114,7 @@ - &dash-ent-10; - - - - - - - - + &dash-ent-16; u7 u6 u5 @@ -2268,11 +2136,7 @@ V4L2_MBUS_FMT_YUYV8_1X16 0x2011 - &dash-ent-10; - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2294,11 +2158,7 @@ - &dash-ent-10; - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2320,11 +2180,7 @@ V4L2_MBUS_FMT_YVYU8_1X16 0x2012 - &dash-ent-10; - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2346,11 +2202,7 @@ - &dash-ent-10; - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2372,10 +2224,7 @@ V4L2_MBUS_FMT_YDYUYDYV8_1X16 0x2014 - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2397,10 +2246,7 @@ - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2422,10 +2268,7 @@ - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2447,10 +2290,7 @@ - - - - - - - - + &dash-ent-16; y7 y6 y5 @@ -2472,7 +2312,7 @@ V4L2_MBUS_FMT_YUYV10_1X20 0x200d - &dash-ent-10; + &dash-ent-12; y9 y8 y7 @@ -2498,7 +2338,7 @@ - &dash-ent-10; + &dash-ent-12; y9 y8 y7 @@ -2524,7 +2364,7 @@ V4L2_MBUS_FMT_YVYU10_1X20 0x200e - &dash-ent-10; + &dash-ent-12; y9 y8 y7 @@ -2550,7 +2390,7 @@ - &dash-ent-10; + &dash-ent-12; y9 y8 y7 @@ -2576,6 +2416,8 @@ V4L2_MBUS_FMT_YUV10_1X30 0x2016 + - + - y9 y8 y7 @@ -2607,6 +2449,43 @@ v1 v0 + + V4L2_MBUS_FMT_AYUV8_1X32 + 0x2017 + + a7 + a6 + a5 + a4 + a3 + a2 + a1 + a0 + y7 + y6 + y5 + y4 + y3 + y2 + y1 + y0 + u7 + u6 + u5 + u4 + u3 + u2 + u1 + u0 + v7 + v6 + v5 + v4 + v3 + v2 + v1 + v0 + diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl index 6a8b7158697f..07e7eea52a0b 100644 --- a/Documentation/DocBook/media_api.tmpl +++ b/Documentation/DocBook/media_api.tmpl @@ -22,8 +22,14 @@ http://linuxtv.org/repo/"> +--------"> ----------"> +------------"> +--------------"> ----------------"> +--------------------"> +----------------------"> +------------------------"> ]> diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h index 6ee63d09b32d..a9601257bb43 100644 --- a/include/uapi/linux/v4l2-mediabus.h +++ b/include/uapi/linux/v4l2-mediabus.h @@ -37,7 +37,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_FIXED = 0x0001, - /* RGB - next is 0x100d */ + /* RGB - next is 0x100e */ V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE = 0x1001, V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE = 0x1002, V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE = 0x1003, @@ -50,8 +50,9 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_RGB888_1X24 = 0x100a, V4L2_MBUS_FMT_RGB888_2X12_BE = 0x100b, V4L2_MBUS_FMT_RGB888_2X12_LE = 0x100c, + V4L2_MBUS_FMT_ARGB8888_1X32 = 0x100d, - /* YUV (including grey) - next is 0x2017 */ + /* YUV (including grey) - next is 0x2018 */ V4L2_MBUS_FMT_Y8_1X8 = 0x2001, V4L2_MBUS_FMT_UV8_1X8 = 0x2015, V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002, @@ -74,6 +75,7 @@ enum v4l2_mbus_pixelcode { V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d, V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e, V4L2_MBUS_FMT_YUV10_1X30 = 0x2016, + V4L2_MBUS_FMT_AYUV8_1X32 = 0x2017, /* Bayer - next is 0x3019 */ V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001, -- cgit v1.2.3-71-gd317 From 8493054844145e47a8f0668f57e2b0073aca850f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 26 Jun 2013 09:46:42 -0300 Subject: [media] v4l: Add V4L2_PIX_FMT_NV16M and V4L2_PIX_FMT_NV61M formats NV16M and NV61M are planar YCbCr 4:2:2 and YCrCb 4:2:2 formats with a luma plane followed by an interleaved chroma plane. The planes are not required to be contiguous in memory, and the formats can only be used with the multi-planar formats API. Signed-off-by: Laurent Pinchart Reviewed-by: Sylwester Nawrocki Reviewed-by: Sakari Ailus Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/pixfmt-nv16m.xml | 171 +++++++++++++++++++++++ Documentation/DocBook/media/v4l/pixfmt.xml | 1 + include/uapi/linux/videodev2.h | 2 + 3 files changed, 174 insertions(+) create mode 100644 Documentation/DocBook/media/v4l/pixfmt-nv16m.xml (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/pixfmt-nv16m.xml b/Documentation/DocBook/media/v4l/pixfmt-nv16m.xml new file mode 100644 index 000000000000..c51d5a4cda09 --- /dev/null +++ b/Documentation/DocBook/media/v4l/pixfmt-nv16m.xml @@ -0,0 +1,171 @@ + + + V4L2_PIX_FMT_NV16M ('NM16'), V4L2_PIX_FMT_NV61M ('NM61') + &manvol; + + + V4L2_PIX_FMT_NV16M + V4L2_PIX_FMT_NV61M + Variation of V4L2_PIX_FMT_NV16 and V4L2_PIX_FMT_NV61 with planes + non contiguous in memory. + + + Description + + This is a multi-planar, two-plane version of the YUV 4:2:0 format. +The three components are separated into two sub-images or planes. +V4L2_PIX_FMT_NV16M differs from V4L2_PIX_FMT_NV16 + in that the two planes are non-contiguous in memory, i.e. the chroma +plane does not necessarily immediately follows the luma plane. +The luminance data occupies the first plane. The Y plane has one byte per pixel. +In the second plane there is chrominance data with alternating chroma samples. +The CbCr plane is the same width and height, in bytes, as the Y plane. +Each CbCr pair belongs to four pixels. For example, +Cb0/Cr0 belongs to +Y'00, Y'01, +Y'10, Y'11. +V4L2_PIX_FMT_NV61M is the same as V4L2_PIX_FMT_NV16M +except the Cb and Cr bytes are swapped, the CrCb plane starts with a Cr byte. + + V4L2_PIX_FMT_NV16M and +V4L2_PIX_FMT_NV61M are intended to be used only in drivers +and applications that support the multi-planar API, described in +. + + + <constant>V4L2_PIX_FMT_NV16M</constant> 4 × 4 pixel image + + + Byte Order. + Each cell is one byte. + + + + + + start0 + 0: + Y'00 + Y'01 + Y'02 + Y'03 + + + start0 + 4: + Y'10 + Y'11 + Y'12 + Y'13 + + + start0 + 8: + Y'20 + Y'21 + Y'22 + Y'23 + + + start0 + 12: + Y'30 + Y'31 + Y'32 + Y'33 + + + + + + start1 + 0: + Cb00 + Cr00 + Cb02 + Cr02 + + + start1 + 4: + Cb10 + Cr10 + Cb12 + Cr12 + + + start1 + 8: + Cb20 + Cr20 + Cb22 + Cr22 + + + start1 + 12: + Cb30 + Cr30 + Cb32 + Cr32 + + + + + + + + + Color Sample Location. + + + + + + + 01 + 23 + + + 0 + YY + YY + + + + C + C + + + 1 + YY + YY + + + + C + C + + + + + + 2 + YY + YY + + + + C + C + + + 3 + YY + YY + + + + C + C + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index 99b8d2ad6e4f..16db350848af 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -718,6 +718,7 @@ information. &sub-nv12m; &sub-nv12mt; &sub-nv16; + &sub-nv16m; &sub-nv24; &sub-m420;
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 95ef4551edc1..fec0c205f38b 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -348,6 +348,8 @@ struct v4l2_pix_format { /* two non contiguous planes - one Y, one Cr + Cb interleaved */ #define V4L2_PIX_FMT_NV12M v4l2_fourcc('N', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 */ #define V4L2_PIX_FMT_NV21M v4l2_fourcc('N', 'M', '2', '1') /* 21 Y/CrCb 4:2:0 */ +#define V4L2_PIX_FMT_NV16M v4l2_fourcc('N', 'M', '1', '6') /* 16 Y/CbCr 4:2:2 */ +#define V4L2_PIX_FMT_NV61M v4l2_fourcc('N', 'M', '6', '1') /* 16 Y/CrCb 4:2:2 */ #define V4L2_PIX_FMT_NV12MT v4l2_fourcc('T', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 64x32 macroblocks */ #define V4L2_PIX_FMT_NV12MT_16X16 v4l2_fourcc('V', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 16x16 macroblocks */ -- cgit v1.2.3-71-gd317 From 26e0ca22c3b85b04f693dd0422f13a61846ccfa9 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 4 Jun 2013 11:22:30 -0300 Subject: [media] v4l: Renesas R-Car VSP1 driver The VSP1 is a video processing engine that includes a blender, scalers, filters and statistics computation. Configurable data path routing logic allows ordering the internal blocks in a flexible way. Due to the configurable nature of the pipeline the driver implements the media controller API and doesn't use the V4L2 mem-to-mem framework, even though the device usually operates in memory to memory mode. Only the read pixel formatters, up/down scalers, write pixel formatters and LCDC interface are supported at this stage. Signed-off-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 10 + drivers/media/platform/Makefile | 2 + drivers/media/platform/vsp1/Makefile | 5 + drivers/media/platform/vsp1/vsp1.h | 73 ++ drivers/media/platform/vsp1/vsp1_drv.c | 492 +++++++++++++ drivers/media/platform/vsp1/vsp1_entity.c | 181 +++++ drivers/media/platform/vsp1/vsp1_entity.h | 68 ++ drivers/media/platform/vsp1/vsp1_lif.c | 238 +++++++ drivers/media/platform/vsp1/vsp1_lif.h | 37 + drivers/media/platform/vsp1/vsp1_regs.h | 581 ++++++++++++++++ drivers/media/platform/vsp1/vsp1_rpf.c | 209 ++++++ drivers/media/platform/vsp1/vsp1_rwpf.c | 124 ++++ drivers/media/platform/vsp1/vsp1_rwpf.h | 53 ++ drivers/media/platform/vsp1/vsp1_uds.c | 346 ++++++++++ drivers/media/platform/vsp1/vsp1_uds.h | 40 ++ drivers/media/platform/vsp1/vsp1_video.c | 1071 +++++++++++++++++++++++++++++ drivers/media/platform/vsp1/vsp1_video.h | 144 ++++ drivers/media/platform/vsp1/vsp1_wpf.c | 233 +++++++ include/linux/platform_data/vsp1.h | 25 + 19 files changed, 3932 insertions(+) create mode 100644 drivers/media/platform/vsp1/Makefile create mode 100644 drivers/media/platform/vsp1/vsp1.h create mode 100644 drivers/media/platform/vsp1/vsp1_drv.c create mode 100644 drivers/media/platform/vsp1/vsp1_entity.c create mode 100644 drivers/media/platform/vsp1/vsp1_entity.h create mode 100644 drivers/media/platform/vsp1/vsp1_lif.c create mode 100644 drivers/media/platform/vsp1/vsp1_lif.h create mode 100644 drivers/media/platform/vsp1/vsp1_regs.h create mode 100644 drivers/media/platform/vsp1/vsp1_rpf.c create mode 100644 drivers/media/platform/vsp1/vsp1_rwpf.c create mode 100644 drivers/media/platform/vsp1/vsp1_rwpf.h create mode 100644 drivers/media/platform/vsp1/vsp1_uds.c create mode 100644 drivers/media/platform/vsp1/vsp1_uds.h create mode 100644 drivers/media/platform/vsp1/vsp1_video.c create mode 100644 drivers/media/platform/vsp1/vsp1_video.h create mode 100644 drivers/media/platform/vsp1/vsp1_wpf.c create mode 100644 include/linux/platform_data/vsp1.h (limited to 'include') diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 08de865cc399..9a44e06c5b31 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -210,6 +210,16 @@ config VIDEO_SH_VEU Support for the Video Engine Unit (VEU) on SuperH and SH-Mobile SoCs. +config VIDEO_RENESAS_VSP1 + tristate "Renesas VSP1 Video Processing Engine" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + ---help--- + This is a V4L2 driver for the Renesas VSP1 video processing engine. + + To compile this driver as a module, choose M here: the module + will be called vsp1. + endif # V4L_MEM2MEM_DRIVERS menuconfig V4L_TEST_DRIVERS diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index eee28dd78d7d..4e4da482c522 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -46,6 +46,8 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o obj-$(CONFIG_SOC_CAMERA) += soc_camera/ +obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ + obj-y += davinci/ obj-$(CONFIG_ARCH_OMAP) += omap/ diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile new file mode 100644 index 000000000000..4da226169e15 --- /dev/null +++ b/drivers/media/platform/vsp1/Makefile @@ -0,0 +1,5 @@ +vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_video.o +vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o +vsp1-y += vsp1_lif.o vsp1_uds.o + +obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h new file mode 100644 index 000000000000..11ac94bec3a3 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1.h @@ -0,0 +1,73 @@ +/* + * vsp1.h -- R-Car VSP1 Driver + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_H__ +#define __VSP1_H__ + +#include +#include +#include +#include + +#include +#include +#include + +#include "vsp1_regs.h" + +struct clk; +struct device; + +struct vsp1_platform_data; +struct vsp1_lif; +struct vsp1_rwpf; +struct vsp1_uds; + +#define VPS1_MAX_RPF 5 +#define VPS1_MAX_UDS 3 +#define VPS1_MAX_WPF 4 + +struct vsp1_device { + struct device *dev; + struct vsp1_platform_data *pdata; + + void __iomem *mmio; + struct clk *clock; + + struct mutex lock; + int ref_count; + + struct vsp1_lif *lif; + struct vsp1_rwpf *rpf[VPS1_MAX_RPF]; + struct vsp1_uds *uds[VPS1_MAX_UDS]; + struct vsp1_rwpf *wpf[VPS1_MAX_WPF]; + + struct list_head entities; + + struct v4l2_device v4l2_dev; + struct media_device media_dev; +}; + +struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1); +void vsp1_device_put(struct vsp1_device *vsp1); + +static inline u32 vsp1_read(struct vsp1_device *vsp1, u32 reg) +{ + return ioread32(vsp1->mmio + reg); +} + +static inline void vsp1_write(struct vsp1_device *vsp1, u32 reg, u32 data) +{ + iowrite32(data, vsp1->mmio + reg); +} + +#endif /* __VSP1_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c new file mode 100644 index 000000000000..e58e49c88415 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -0,0 +1,492 @@ +/* + * vsp1_drv.c -- R-Car VSP1 Driver + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "vsp1.h" +#include "vsp1_lif.h" +#include "vsp1_rwpf.h" +#include "vsp1_uds.h" + +/* ----------------------------------------------------------------------------- + * Interrupt Handling + */ + +static irqreturn_t vsp1_irq_handler(int irq, void *data) +{ + u32 mask = VI6_WFP_IRQ_STA_DFE | VI6_WFP_IRQ_STA_FRE; + struct vsp1_device *vsp1 = data; + irqreturn_t ret = IRQ_NONE; + unsigned int i; + + for (i = 0; i < VPS1_MAX_WPF; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + u32 status; + + if (wpf == NULL) + continue; + + pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity); + status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i)); + vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask); + + if (status & VI6_WFP_IRQ_STA_FRE) { + vsp1_pipeline_frame_end(pipe); + ret = IRQ_HANDLED; + } + } + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Entities + */ + +/* + * vsp1_create_links - Create links from all sources to the given sink + * + * This function creates media links from all valid sources to the given sink + * pad. Links that would be invalid according to the VSP1 hardware capabilities + * are skipped. Those include all links + * + * - from a UDS to a UDS (UDS entities can't be chained) + * - from an entity to itself (no loops are allowed) + */ +static int vsp1_create_links(struct vsp1_device *vsp1, struct vsp1_entity *sink) +{ + struct media_entity *entity = &sink->subdev.entity; + struct vsp1_entity *source; + unsigned int pad; + int ret; + + list_for_each_entry(source, &vsp1->entities, list_dev) { + u32 flags; + + if (source->type == sink->type) + continue; + + if (source->type == VSP1_ENTITY_LIF || + source->type == VSP1_ENTITY_WPF) + continue; + + flags = source->type == VSP1_ENTITY_RPF && + sink->type == VSP1_ENTITY_WPF && + source->index == sink->index + ? MEDIA_LNK_FL_ENABLED : 0; + + for (pad = 0; pad < entity->num_pads; ++pad) { + if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK)) + continue; + + ret = media_entity_create_link(&source->subdev.entity, + source->source_pad, + entity, pad, flags); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static void vsp1_destroy_entities(struct vsp1_device *vsp1) +{ + struct vsp1_entity *entity; + struct vsp1_entity *next; + + list_for_each_entry_safe(entity, next, &vsp1->entities, list_dev) { + list_del(&entity->list_dev); + vsp1_entity_destroy(entity); + } + + v4l2_device_unregister(&vsp1->v4l2_dev); + media_device_unregister(&vsp1->media_dev); +} + +static int vsp1_create_entities(struct vsp1_device *vsp1) +{ + struct media_device *mdev = &vsp1->media_dev; + struct v4l2_device *vdev = &vsp1->v4l2_dev; + struct vsp1_entity *entity; + unsigned int i; + int ret; + + mdev->dev = vsp1->dev; + strlcpy(mdev->model, "VSP1", sizeof(mdev->model)); + ret = media_device_register(mdev); + if (ret < 0) { + dev_err(vsp1->dev, "media device registration failed (%d)\n", + ret); + return ret; + } + + vdev->mdev = mdev; + ret = v4l2_device_register(vsp1->dev, vdev); + if (ret < 0) { + dev_err(vsp1->dev, "V4L2 device registration failed (%d)\n", + ret); + goto done; + } + + /* Instantiate all the entities. */ + if (vsp1->pdata->features & VSP1_HAS_LIF) { + vsp1->lif = vsp1_lif_create(vsp1); + if (IS_ERR(vsp1->lif)) { + ret = PTR_ERR(vsp1->lif); + goto done; + } + + list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities); + } + + for (i = 0; i < vsp1->pdata->rpf_count; ++i) { + struct vsp1_rwpf *rpf; + + rpf = vsp1_rpf_create(vsp1, i); + if (IS_ERR(rpf)) { + ret = PTR_ERR(rpf); + goto done; + } + + vsp1->rpf[i] = rpf; + list_add_tail(&rpf->entity.list_dev, &vsp1->entities); + } + + for (i = 0; i < vsp1->pdata->uds_count; ++i) { + struct vsp1_uds *uds; + + uds = vsp1_uds_create(vsp1, i); + if (IS_ERR(uds)) { + ret = PTR_ERR(uds); + goto done; + } + + vsp1->uds[i] = uds; + list_add_tail(&uds->entity.list_dev, &vsp1->entities); + } + + for (i = 0; i < vsp1->pdata->wpf_count; ++i) { + struct vsp1_rwpf *wpf; + + wpf = vsp1_wpf_create(vsp1, i); + if (IS_ERR(wpf)) { + ret = PTR_ERR(wpf); + goto done; + } + + vsp1->wpf[i] = wpf; + list_add_tail(&wpf->entity.list_dev, &vsp1->entities); + } + + /* Create links. */ + list_for_each_entry(entity, &vsp1->entities, list_dev) { + if (entity->type == VSP1_ENTITY_LIF || + entity->type == VSP1_ENTITY_RPF) + continue; + + ret = vsp1_create_links(vsp1, entity); + if (ret < 0) + goto done; + } + + if (vsp1->pdata->features & VSP1_HAS_LIF) { + ret = media_entity_create_link( + &vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE, + &vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0); + if (ret < 0) + return ret; + } + + /* Register all subdevs. */ + list_for_each_entry(entity, &vsp1->entities, list_dev) { + ret = v4l2_device_register_subdev(&vsp1->v4l2_dev, + &entity->subdev); + if (ret < 0) + goto done; + } + + ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev); + +done: + if (ret < 0) + vsp1_destroy_entities(vsp1); + + return ret; +} + +static int vsp1_device_init(struct vsp1_device *vsp1) +{ + unsigned int i; + u32 status; + + /* Reset any channel that might be running. */ + status = vsp1_read(vsp1, VI6_STATUS); + + for (i = 0; i < VPS1_MAX_WPF; ++i) { + unsigned int timeout; + + if (!(status & VI6_STATUS_SYS_ACT(i))) + continue; + + vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(i)); + for (timeout = 10; timeout > 0; --timeout) { + status = vsp1_read(vsp1, VI6_STATUS); + if (!(status & VI6_STATUS_SYS_ACT(i))) + break; + + usleep_range(1000, 2000); + } + + if (!timeout) { + dev_err(vsp1->dev, "failed to reset wpf.%u\n", i); + return -ETIMEDOUT; + } + } + + vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) | + (8 << VI6_CLK_DCSWT_CSTRW_SHIFT)); + + for (i = 0; i < VPS1_MAX_RPF; ++i) + vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED); + + for (i = 0; i < VPS1_MAX_UDS; ++i) + vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED); + + vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED); + vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED); + vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED); + vsp1_write(vsp1, VI6_DPR_HST_ROUTE, VI6_DPR_NODE_UNUSED); + vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED); + vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED); + + vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) | + (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT)); + vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) | + (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT)); + + return 0; +} + +/* + * vsp1_device_get - Acquire the VSP1 device + * + * Increment the VSP1 reference count and initialize the device if the first + * reference is taken. + * + * Return a pointer to the VSP1 device or NULL if an error occured. + */ +struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1) +{ + struct vsp1_device *__vsp1 = vsp1; + int ret; + + mutex_lock(&vsp1->lock); + if (vsp1->ref_count > 0) + goto done; + + ret = clk_prepare_enable(vsp1->clock); + if (ret < 0) { + __vsp1 = NULL; + goto done; + } + + ret = vsp1_device_init(vsp1); + if (ret < 0) { + clk_disable_unprepare(vsp1->clock); + __vsp1 = NULL; + goto done; + } + +done: + if (__vsp1) + vsp1->ref_count++; + + mutex_unlock(&vsp1->lock); + return __vsp1; +} + +/* + * vsp1_device_put - Release the VSP1 device + * + * Decrement the VSP1 reference count and cleanup the device if the last + * reference is released. + */ +void vsp1_device_put(struct vsp1_device *vsp1) +{ + mutex_lock(&vsp1->lock); + + if (--vsp1->ref_count == 0) + clk_disable_unprepare(vsp1->clock); + + mutex_unlock(&vsp1->lock); +} + +/* ----------------------------------------------------------------------------- + * Power Management + */ + +#ifdef CONFIG_PM_SLEEP +static int vsp1_pm_suspend(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + + WARN_ON(mutex_is_locked(&vsp1->lock)); + + if (vsp1->ref_count == 0) + return 0; + + clk_disable_unprepare(vsp1->clock); + return 0; +} + +static int vsp1_pm_resume(struct device *dev) +{ + struct vsp1_device *vsp1 = dev_get_drvdata(dev); + + WARN_ON(mutex_is_locked(&vsp1->lock)); + + if (vsp1->ref_count) + return 0; + + return clk_prepare_enable(vsp1->clock); +} +#endif + +static const struct dev_pm_ops vsp1_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume) +}; + +/* ----------------------------------------------------------------------------- + * Platform Driver + */ + +static struct vsp1_platform_data * +vsp1_get_platform_data(struct platform_device *pdev) +{ + struct vsp1_platform_data *pdata = pdev->dev.platform_data; + + if (pdata == NULL) { + dev_err(&pdev->dev, "missing platform data\n"); + return NULL; + } + + if (pdata->rpf_count <= 0 || pdata->rpf_count > VPS1_MAX_RPF) { + dev_err(&pdev->dev, "invalid number of RPF (%u)\n", + pdata->rpf_count); + return NULL; + } + + if (pdata->uds_count <= 0 || pdata->uds_count > VPS1_MAX_UDS) { + dev_err(&pdev->dev, "invalid number of UDS (%u)\n", + pdata->uds_count); + return NULL; + } + + if (pdata->wpf_count <= 0 || pdata->wpf_count > VPS1_MAX_WPF) { + dev_err(&pdev->dev, "invalid number of WPF (%u)\n", + pdata->wpf_count); + return NULL; + } + + return pdata; +} + +static int vsp1_probe(struct platform_device *pdev) +{ + struct vsp1_device *vsp1; + struct resource *irq; + struct resource *io; + int ret; + + vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL); + if (vsp1 == NULL) + return -ENOMEM; + + vsp1->dev = &pdev->dev; + mutex_init(&vsp1->lock); + INIT_LIST_HEAD(&vsp1->entities); + + vsp1->pdata = vsp1_get_platform_data(pdev); + if (vsp1->pdata == NULL) + return -ENODEV; + + /* I/O, IRQ and clock resources */ + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + vsp1->mmio = devm_ioremap_resource(&pdev->dev, io); + if (IS_ERR((void *)vsp1->mmio)) + return PTR_ERR((void *)vsp1->mmio); + + vsp1->clock = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(vsp1->clock)) { + dev_err(&pdev->dev, "failed to get clock\n"); + return PTR_ERR(vsp1->clock); + } + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "missing IRQ\n"); + return -EINVAL; + } + + ret = devm_request_irq(&pdev->dev, irq->start, vsp1_irq_handler, + IRQF_SHARED, dev_name(&pdev->dev), vsp1); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + return ret; + } + + /* Instanciate entities */ + ret = vsp1_create_entities(vsp1); + if (ret < 0) { + dev_err(&pdev->dev, "failed to create entities\n"); + return ret; + } + + platform_set_drvdata(pdev, vsp1); + + return 0; +} + +static int vsp1_remove(struct platform_device *pdev) +{ + struct vsp1_device *vsp1 = platform_get_drvdata(pdev); + + vsp1_destroy_entities(vsp1); + + return 0; +} + +static struct platform_driver vsp1_platform_driver = { + .probe = vsp1_probe, + .remove = vsp1_remove, + .driver = { + .owner = THIS_MODULE, + .name = "vsp1", + .pm = &vsp1_pm_ops, + }, +}; + +module_platform_driver(vsp1_platform_driver); + +MODULE_ALIAS("vsp1"); +MODULE_AUTHOR("Laurent Pinchart "); +MODULE_DESCRIPTION("Renesas VSP1 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c new file mode 100644 index 000000000000..9028f9d524f4 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -0,0 +1,181 @@ +/* + * vsp1_entity.c -- R-Car VSP1 Base Entity + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include +#include + +#include "vsp1.h" +#include "vsp1_entity.h" + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +struct v4l2_mbus_framefmt * +vsp1_entity_get_pad_format(struct vsp1_entity *entity, + struct v4l2_subdev_fh *fh, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(fh, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &entity->formats[pad]; + default: + return NULL; + } +} + +/* + * vsp1_entity_init_formats - Initialize formats on all pads + * @subdev: V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +void vsp1_entity_init_formats(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + unsigned int pad; + + for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { + memset(&format, 0, sizeof(format)); + + format.pad = pad; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY + : V4L2_SUBDEV_FORMAT_ACTIVE; + + v4l2_subdev_call(subdev, pad, set_fmt, fh, &format); + } +} + +static int vsp1_entity_open(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + vsp1_entity_init_formats(subdev, fh); + + return 0; +} + +const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops = { + .open = vsp1_entity_open, +}; + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static int vsp1_entity_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct vsp1_entity *source; + + if (!(local->flags & MEDIA_PAD_FL_SOURCE)) + return 0; + + source = container_of(local->entity, struct vsp1_entity, subdev.entity); + + if (!source->route) + return 0; + + if (flags & MEDIA_LNK_FL_ENABLED) { + if (source->sink) + return -EBUSY; + source->sink = remote->entity; + } else { + source->sink = NULL; + } + + return 0; +} + +const struct media_entity_operations vsp1_media_ops = { + .link_setup = vsp1_entity_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +/* ----------------------------------------------------------------------------- + * Initialization + */ + +int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, + unsigned int num_pads) +{ + static const struct { + unsigned int id; + unsigned int reg; + } routes[] = { + { VI6_DPR_NODE_LIF, 0 }, + { VI6_DPR_NODE_RPF(0), VI6_DPR_RPF_ROUTE(0) }, + { VI6_DPR_NODE_RPF(1), VI6_DPR_RPF_ROUTE(1) }, + { VI6_DPR_NODE_RPF(2), VI6_DPR_RPF_ROUTE(2) }, + { VI6_DPR_NODE_RPF(3), VI6_DPR_RPF_ROUTE(3) }, + { VI6_DPR_NODE_RPF(4), VI6_DPR_RPF_ROUTE(4) }, + { VI6_DPR_NODE_UDS(0), VI6_DPR_UDS_ROUTE(0) }, + { VI6_DPR_NODE_UDS(1), VI6_DPR_UDS_ROUTE(1) }, + { VI6_DPR_NODE_UDS(2), VI6_DPR_UDS_ROUTE(2) }, + { VI6_DPR_NODE_WPF(0), 0 }, + { VI6_DPR_NODE_WPF(1), 0 }, + { VI6_DPR_NODE_WPF(2), 0 }, + { VI6_DPR_NODE_WPF(3), 0 }, + }; + + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(routes); ++i) { + if (routes[i].id == entity->id) { + entity->route = routes[i].reg; + break; + } + } + + if (i == ARRAY_SIZE(routes)) + return -EINVAL; + + entity->vsp1 = vsp1; + entity->source_pad = num_pads - 1; + + /* Allocate formats and pads. */ + entity->formats = devm_kzalloc(vsp1->dev, + num_pads * sizeof(*entity->formats), + GFP_KERNEL); + if (entity->formats == NULL) + return -ENOMEM; + + entity->pads = devm_kzalloc(vsp1->dev, num_pads * sizeof(*entity->pads), + GFP_KERNEL); + if (entity->pads == NULL) + return -ENOMEM; + + /* Initialize pads. */ + for (i = 0; i < num_pads - 1; ++i) + entity->pads[i].flags = MEDIA_PAD_FL_SINK; + + entity->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE; + + /* Initialize the media entity. */ + return media_entity_init(&entity->subdev.entity, num_pads, + entity->pads, 0); +} + +void vsp1_entity_destroy(struct vsp1_entity *entity) +{ + media_entity_cleanup(&entity->subdev.entity); +} diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h new file mode 100644 index 000000000000..c4feab2cbb81 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -0,0 +1,68 @@ +/* + * vsp1_entity.h -- R-Car VSP1 Base Entity + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_ENTITY_H__ +#define __VSP1_ENTITY_H__ + +#include + +#include + +struct vsp1_device; + +enum vsp1_entity_type { + VSP1_ENTITY_LIF, + VSP1_ENTITY_RPF, + VSP1_ENTITY_UDS, + VSP1_ENTITY_WPF, +}; + +struct vsp1_entity { + struct vsp1_device *vsp1; + + enum vsp1_entity_type type; + unsigned int index; + unsigned int id; + unsigned int route; + + struct list_head list_dev; + struct list_head list_pipe; + + struct media_pad *pads; + unsigned int source_pad; + + struct media_entity *sink; + + struct v4l2_subdev subdev; + struct v4l2_mbus_framefmt *formats; +}; + +static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_entity, subdev); +} + +int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, + unsigned int num_pads); +void vsp1_entity_destroy(struct vsp1_entity *entity); + +extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops; +extern const struct media_entity_operations vsp1_media_ops; + +struct v4l2_mbus_framefmt * +vsp1_entity_get_pad_format(struct vsp1_entity *entity, + struct v4l2_subdev_fh *fh, + unsigned int pad, u32 which); +void vsp1_entity_init_formats(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh); + +#endif /* __VSP1_ENTITY_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c new file mode 100644 index 000000000000..74a32e69ef10 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -0,0 +1,238 @@ +/* + * vsp1_lif.c -- R-Car VSP1 LCD Controller Interface + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include + +#include "vsp1.h" +#include "vsp1_lif.h" + +#define LIF_MIN_SIZE 2U +#define LIF_MAX_SIZE 2048U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_lif_read(struct vsp1_lif *lif, u32 reg) +{ + return vsp1_read(lif->entity.vsp1, reg); +} + +static inline void vsp1_lif_write(struct vsp1_lif *lif, u32 reg, u32 data) +{ + vsp1_write(lif->entity.vsp1, reg, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static int lif_s_stream(struct v4l2_subdev *subdev, int enable) +{ + const struct v4l2_mbus_framefmt *format; + struct vsp1_lif *lif = to_lif(subdev); + unsigned int hbth = 1300; + unsigned int obth = 400; + unsigned int lbth = 200; + + if (!enable) { + vsp1_lif_write(lif, VI6_LIF_CTRL, 0); + return 0; + } + + format = &lif->entity.formats[LIF_PAD_SOURCE]; + + obth = min(obth, (format->width + 1) / 2 * format->height - 4); + + vsp1_lif_write(lif, VI6_LIF_CSBTH, + (hbth << VI6_LIF_CSBTH_HBTH_SHIFT) | + (lbth << VI6_LIF_CSBTH_LBTH_SHIFT)); + + vsp1_lif_write(lif, VI6_LIF_CTRL, + (obth << VI6_LIF_CTRL_OBTH_SHIFT) | + (format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) | + VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int lif_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const unsigned int codes[] = { + V4L2_MBUS_FMT_ARGB8888_1X32, + V4L2_MBUS_FMT_AYUV8_1X32, + }; + + if (code->pad == LIF_PAD_SINK) { + if (code->index >= ARRAY_SIZE(codes)) + return -EINVAL; + + code->code = codes[code->index]; + } else { + struct v4l2_mbus_framefmt *format; + + /* The LIF can't perform format conversion, the sink format is + * always identical to the source format. + */ + if (code->index) + return -EINVAL; + + format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK); + code->code = format->code; + } + + return 0; +} + +static int lif_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == LIF_PAD_SINK) { + fse->min_width = LIF_MIN_SIZE; + fse->max_width = LIF_MAX_SIZE; + fse->min_height = LIF_MIN_SIZE; + fse->max_height = LIF_MAX_SIZE; + } else { + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} + +static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_lif *lif = to_lif(subdev); + + fmt->format = *vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad, + fmt->which); + + return 0; +} + +static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_lif *lif = to_lif(subdev); + struct v4l2_mbus_framefmt *format; + + /* Default to YUV if the requested format is not supported. */ + if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 && + fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32) + fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32; + + format = vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad, + fmt->which); + + if (fmt->pad == LIF_PAD_SOURCE) { + /* The LIF source format is always identical to its sink + * format. + */ + fmt->format = *format; + return 0; + } + + format->code = fmt->format.code; + format->width = clamp_t(unsigned int, fmt->format.width, + LIF_MIN_SIZE, LIF_MAX_SIZE); + format->height = clamp_t(unsigned int, fmt->format.height, + LIF_MIN_SIZE, LIF_MAX_SIZE); + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(&lif->entity, fh, LIF_PAD_SOURCE, + fmt->which); + *format = fmt->format; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops lif_video_ops = { + .s_stream = lif_s_stream, +}; + +static struct v4l2_subdev_pad_ops lif_pad_ops = { + .enum_mbus_code = lif_enum_mbus_code, + .enum_frame_size = lif_enum_frame_size, + .get_fmt = lif_get_format, + .set_fmt = lif_set_format, +}; + +static struct v4l2_subdev_ops lif_ops = { + .video = &lif_video_ops, + .pad = &lif_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1) +{ + struct v4l2_subdev *subdev; + struct vsp1_lif *lif; + int ret; + + lif = devm_kzalloc(vsp1->dev, sizeof(*lif), GFP_KERNEL); + if (lif == NULL) + return ERR_PTR(-ENOMEM); + + lif->entity.type = VSP1_ENTITY_LIF; + lif->entity.id = VI6_DPR_NODE_LIF; + + ret = vsp1_entity_init(vsp1, &lif->entity, 2); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the V4L2 subdev. */ + subdev = &lif->entity.subdev; + v4l2_subdev_init(subdev, &lif_ops); + + subdev->entity.ops = &vsp1_media_ops; + subdev->internal_ops = &vsp1_subdev_internal_ops; + snprintf(subdev->name, sizeof(subdev->name), "%s lif", + dev_name(vsp1->dev)); + v4l2_set_subdevdata(subdev, lif); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + vsp1_entity_init_formats(subdev, NULL); + + return lif; +} diff --git a/drivers/media/platform/vsp1/vsp1_lif.h b/drivers/media/platform/vsp1/vsp1_lif.h new file mode 100644 index 000000000000..89b93af56fdc --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_lif.h @@ -0,0 +1,37 @@ +/* + * vsp1_lif.h -- R-Car VSP1 LCD Controller Interface + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_LIF_H__ +#define __VSP1_LIF_H__ + +#include +#include + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define LIF_PAD_SINK 0 +#define LIF_PAD_SOURCE 1 + +struct vsp1_lif { + struct vsp1_entity entity; +}; + +static inline struct vsp1_lif *to_lif(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_lif, entity.subdev); +} + +struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1); + +#endif /* __VSP1_LIF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h new file mode 100644 index 000000000000..1d3304f1365b --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -0,0 +1,581 @@ +/* + * vsp1_regs.h -- R-Car VSP1 Registers Definitions + * + * Copyright (C) 2013 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + */ + +#ifndef __VSP1_REGS_H__ +#define __VSP1_REGS_H__ + +/* ----------------------------------------------------------------------------- + * General Control Registers + */ + +#define VI6_CMD(n) (0x0000 + (n) * 4) +#define VI6_CMD_STRCMD (1 << 0) + +#define VI6_CLK_DCSWT 0x0018 +#define VI6_CLK_DCSWT_CSTPW_MASK (0xff << 8) +#define VI6_CLK_DCSWT_CSTPW_SHIFT 8 +#define VI6_CLK_DCSWT_CSTRW_MASK (0xff << 0) +#define VI6_CLK_DCSWT_CSTRW_SHIFT 0 + +#define VI6_SRESET 0x0028 +#define VI6_SRESET_SRTS(n) (1 << (n)) + +#define VI6_STATUS 0x0038 +#define VI6_STATUS_SYS_ACT(n) (1 << ((n) + 8)) + +#define VI6_WPF_IRQ_ENB(n) (0x0048 + (n) * 12) +#define VI6_WFP_IRQ_ENB_DFEE (1 << 1) +#define VI6_WFP_IRQ_ENB_FREE (1 << 0) + +#define VI6_WPF_IRQ_STA(n) (0x004c + (n) * 12) +#define VI6_WFP_IRQ_STA_DFE (1 << 1) +#define VI6_WFP_IRQ_STA_FRE (1 << 0) + +#define VI6_DISP_IRQ_ENB 0x0078 +#define VI6_DISP_IRQ_ENB_DSTE (1 << 8) +#define VI6_DISP_IRQ_ENB_MAEE (1 << 5) +#define VI6_DISP_IRQ_ENB_LNEE(n) (1 << ((n) + 4)) + +#define VI6_DISP_IRQ_STA 0x007c +#define VI6_DISP_IRQ_STA_DSE (1 << 8) +#define VI6_DISP_IRQ_STA_MAE (1 << 5) +#define VI6_DISP_IRQ_STA_LNE(n) (1 << ((n) + 4)) + +#define VI6_WPF_LINE_COUNT(n) (0x0084 + (n) * 4) +#define VI6_WPF_LINE_COUNT_MASK (0x1fffff << 0) + +/* ----------------------------------------------------------------------------- + * Display List Control Registers + */ + +#define VI6_DL_CTRL 0x0100 +#define VI6_DL_CTRL_AR_WAIT_MASK (0xffff << 16) +#define VI6_DL_CTRL_AR_WAIT_SHIFT 16 +#define VI6_DL_CTRL_DC2 (1 << 12) +#define VI6_DL_CTRL_DC1 (1 << 8) +#define VI6_DL_CTRL_DC0 (1 << 4) +#define VI6_DL_CTRL_CFM0 (1 << 2) +#define VI6_DL_CTRL_NH0 (1 << 1) +#define VI6_DL_CTRL_DLE (1 << 0) + +#define VI6_DL_HDR_ADDR(n) (0x0104 + (n) * 4) + +#define VI6_DL_SWAP 0x0114 +#define VI6_DL_SWAP_LWS (1 << 2) +#define VI6_DL_SWAP_WDS (1 << 1) +#define VI6_DL_SWAP_BTS (1 << 0) + +#define VI6_DL_EXT_CTRL 0x011c +#define VI6_DL_EXT_CTRL_NWE (1 << 16) +#define VI6_DL_EXT_CTRL_POLINT_MASK (0x3f << 8) +#define VI6_DL_EXT_CTRL_POLINT_SHIFT 8 +#define VI6_DL_EXT_CTRL_DLPRI (1 << 5) +#define VI6_DL_EXT_CTRL_EXPRI (1 << 4) +#define VI6_DL_EXT_CTRL_EXT (1 << 0) + +#define VI6_DL_BODY_SIZE 0x0120 +#define VI6_DL_BODY_SIZE_UPD (1 << 24) +#define VI6_DL_BODY_SIZE_BS_MASK (0x1ffff << 0) +#define VI6_DL_BODY_SIZE_BS_SHIFT 0 + +/* ----------------------------------------------------------------------------- + * RPF Control Registers + */ + +#define VI6_RPF_OFFSET 0x100 + +#define VI6_RPF_SRC_BSIZE 0x0300 +#define VI6_RPF_SRC_BSIZE_BHSIZE_MASK (0x1fff << 16) +#define VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT 16 +#define VI6_RPF_SRC_BSIZE_BVSIZE_MASK (0x1fff << 0) +#define VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT 0 + +#define VI6_RPF_SRC_ESIZE 0x0304 +#define VI6_RPF_SRC_ESIZE_EHSIZE_MASK (0x1fff << 16) +#define VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT 16 +#define VI6_RPF_SRC_ESIZE_EVSIZE_MASK (0x1fff << 0) +#define VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT 0 + +#define VI6_RPF_INFMT 0x0308 +#define VI6_RPF_INFMT_VIR (1 << 28) +#define VI6_RPF_INFMT_CIPM (1 << 16) +#define VI6_RPF_INFMT_SPYCS (1 << 15) +#define VI6_RPF_INFMT_SPUVS (1 << 14) +#define VI6_RPF_INFMT_CEXT_ZERO (0 << 12) +#define VI6_RPF_INFMT_CEXT_EXT (1 << 12) +#define VI6_RPF_INFMT_CEXT_ONE (2 << 12) +#define VI6_RPF_INFMT_CEXT_MASK (3 << 12) +#define VI6_RPF_INFMT_RDTM_BT601 (0 << 9) +#define VI6_RPF_INFMT_RDTM_BT601_EXT (1 << 9) +#define VI6_RPF_INFMT_RDTM_BT709 (2 << 9) +#define VI6_RPF_INFMT_RDTM_BT709_EXT (3 << 9) +#define VI6_RPF_INFMT_RDTM_MASK (7 << 9) +#define VI6_RPF_INFMT_CSC (1 << 8) +#define VI6_RPF_INFMT_RDFMT_MASK (0x7f << 0) +#define VI6_RPF_INFMT_RDFMT_SHIFT 0 + +#define VI6_RPF_DSWAP 0x030c +#define VI6_RPF_DSWAP_A_LLS (1 << 11) +#define VI6_RPF_DSWAP_A_LWS (1 << 10) +#define VI6_RPF_DSWAP_A_WDS (1 << 9) +#define VI6_RPF_DSWAP_A_BTS (1 << 8) +#define VI6_RPF_DSWAP_P_LLS (1 << 3) +#define VI6_RPF_DSWAP_P_LWS (1 << 2) +#define VI6_RPF_DSWAP_P_WDS (1 << 1) +#define VI6_RPF_DSWAP_P_BTS (1 << 0) + +#define VI6_RPF_LOC 0x0310 +#define VI6_RPF_LOC_HCOORD_MASK (0x1fff << 16) +#define VI6_RPF_LOC_HCOORD_SHIFT 16 +#define VI6_RPF_LOC_VCOORD_MASK (0x1fff << 0) +#define VI6_RPF_LOC_VCOORD_SHIFT 0 + +#define VI6_RPF_ALPH_SEL 0x0314 +#define VI6_RPF_ALPH_SEL_ASEL_PACKED (0 << 28) +#define VI6_RPF_ALPH_SEL_ASEL_8B_PLANE (1 << 28) +#define VI6_RPF_ALPH_SEL_ASEL_SELECT (2 << 28) +#define VI6_RPF_ALPH_SEL_ASEL_1B_PLANE (3 << 28) +#define VI6_RPF_ALPH_SEL_ASEL_FIXED (4 << 28) +#define VI6_RPF_ALPH_SEL_ASEL_MASK (7 << 28) +#define VI6_RPF_ALPH_SEL_ASEL_SHIFT 28 +#define VI6_RPF_ALPH_SEL_IROP_MASK (0xf << 24) +#define VI6_RPF_ALPH_SEL_IROP_SHIFT 24 +#define VI6_RPF_ALPH_SEL_BSEL (1 << 23) +#define VI6_RPF_ALPH_SEL_AEXT_ZERO (0 << 18) +#define VI6_RPF_ALPH_SEL_AEXT_EXT (1 << 18) +#define VI6_RPF_ALPH_SEL_AEXT_ONE (2 << 18) +#define VI6_RPF_ALPH_SEL_AEXT_MASK (3 << 18) +#define VI6_RPF_ALPH_SEL_ALPHA0_MASK (0xff << 8) +#define VI6_RPF_ALPH_SEL_ALPHA0_SHIFT 8 +#define VI6_RPF_ALPH_SEL_ALPHA1_MASK (0xff << 0) +#define VI6_RPF_ALPH_SEL_ALPHA1_SHIFT 0 + +#define VI6_RPF_VRTCOL_SET 0x0318 +#define VI6_RPF_VRTCOL_SET_LAYA_MASK (0xff << 24) +#define VI6_RPF_VRTCOL_SET_LAYA_SHIFT 24 +#define VI6_RPF_VRTCOL_SET_LAYR_MASK (0xff << 16) +#define VI6_RPF_VRTCOL_SET_LAYR_SHIFT 16 +#define VI6_RPF_VRTCOL_SET_LAYG_MASK (0xff << 8) +#define VI6_RPF_VRTCOL_SET_LAYG_SHIFT 8 +#define VI6_RPF_VRTCOL_SET_LAYB_MASK (0xff << 0) +#define VI6_RPF_VRTCOL_SET_LAYB_SHIFT 0 + +#define VI6_RPF_MSK_CTRL 0x031c +#define VI6_RPF_MSK_CTRL_MSK_EN (1 << 24) +#define VI6_RPF_MSK_CTRL_MGR_MASK (0xff << 16) +#define VI6_RPF_MSK_CTRL_MGR_SHIFT 16 +#define VI6_RPF_MSK_CTRL_MGG_MASK (0xff << 8) +#define VI6_RPF_MSK_CTRL_MGG_SHIFT 8 +#define VI6_RPF_MSK_CTRL_MGB_MASK (0xff << 0) +#define VI6_RPF_MSK_CTRL_MGB_SHIFT 0 + +#define VI6_RPF_MSK_SET0 0x0320 +#define VI6_RPF_MSK_SET1 0x0324 +#define VI6_RPF_MSK_SET_MSA_MASK (0xff << 24) +#define VI6_RPF_MSK_SET_MSA_SHIFT 24 +#define VI6_RPF_MSK_SET_MSR_MASK (0xff << 16) +#define VI6_RPF_MSK_SET_MSR_SHIFT 16 +#define VI6_RPF_MSK_SET_MSG_MASK (0xff << 8) +#define VI6_RPF_MSK_SET_MSG_SHIFT 8 +#define VI6_RPF_MSK_SET_MSB_MASK (0xff << 0) +#define VI6_RPF_MSK_SET_MSB_SHIFT 0 + +#define VI6_RPF_CKEY_CTRL 0x0328 +#define VI6_RPF_CKEY_CTRL_CV (1 << 4) +#define VI6_RPF_CKEY_CTRL_SAPE1 (1 << 1) +#define VI6_RPF_CKEY_CTRL_SAPE0 (1 << 0) + +#define VI6_RPF_CKEY_SET0 0x032c +#define VI6_RPF_CKEY_SET1 0x0330 +#define VI6_RPF_CKEY_SET_AP_MASK (0xff << 24) +#define VI6_RPF_CKEY_SET_AP_SHIFT 24 +#define VI6_RPF_CKEY_SET_R_MASK (0xff << 16) +#define VI6_RPF_CKEY_SET_R_SHIFT 16 +#define VI6_RPF_CKEY_SET_GY_MASK (0xff << 8) +#define VI6_RPF_CKEY_SET_GY_SHIFT 8 +#define VI6_RPF_CKEY_SET_B_MASK (0xff << 0) +#define VI6_RPF_CKEY_SET_B_SHIFT 0 + +#define VI6_RPF_SRCM_PSTRIDE 0x0334 +#define VI6_RPF_SRCM_PSTRIDE_Y_SHIFT 16 +#define VI6_RPF_SRCM_PSTRIDE_C_SHIFT 0 + +#define VI6_RPF_SRCM_ASTRIDE 0x0338 +#define VI6_RPF_SRCM_PSTRIDE_A_SHIFT 0 + +#define VI6_RPF_SRCM_ADDR_Y 0x033c +#define VI6_RPF_SRCM_ADDR_C0 0x0340 +#define VI6_RPF_SRCM_ADDR_C1 0x0344 +#define VI6_RPF_SRCM_ADDR_AI 0x0348 + +/* ----------------------------------------------------------------------------- + * WPF Control Registers + */ + +#define VI6_WPF_OFFSET 0x100 + +#define VI6_WPF_SRCRPF 0x1000 +#define VI6_WPF_SRCRPF_VIRACT_DIS (0 << 28) +#define VI6_WPF_SRCRPF_VIRACT_SUB (1 << 28) +#define VI6_WPF_SRCRPF_VIRACT_MST (2 << 28) +#define VI6_WPF_SRCRPF_VIRACT_MASK (3 << 28) +#define VI6_WPF_SRCRPF_RPF_ACT_DIS(n) (0 << ((n) * 2)) +#define VI6_WPF_SRCRPF_RPF_ACT_SUB(n) (1 << ((n) * 2)) +#define VI6_WPF_SRCRPF_RPF_ACT_MST(n) (2 << ((n) * 2)) +#define VI6_WPF_SRCRPF_RPF_ACT_MASK(n) (3 << ((n) * 2)) + +#define VI6_WPF_HSZCLIP 0x1004 +#define VI6_WPF_VSZCLIP 0x1008 +#define VI6_WPF_SZCLIP_EN (1 << 28) +#define VI6_WPF_SZCLIP_OFST_MASK (0xff << 16) +#define VI6_WPF_SZCLIP_OFST_SHIFT 16 +#define VI6_WPF_SZCLIP_SIZE_MASK (0x1fff << 0) +#define VI6_WPF_SZCLIP_SIZE_SHIFT 0 + +#define VI6_WPF_OUTFMT 0x100c +#define VI6_WPF_OUTFMT_PDV_MASK (0xff << 24) +#define VI6_WPF_OUTFMT_PDV_SHIFT 24 +#define VI6_WPF_OUTFMT_PXA (1 << 23) +#define VI6_WPF_OUTFMT_FLP (1 << 16) +#define VI6_WPF_OUTFMT_SPYCS (1 << 15) +#define VI6_WPF_OUTFMT_SPUVS (1 << 14) +#define VI6_WPF_OUTFMT_DITH_DIS (0 << 12) +#define VI6_WPF_OUTFMT_DITH_EN (3 << 12) +#define VI6_WPF_OUTFMT_DITH_MASK (3 << 12) +#define VI6_WPF_OUTFMT_WRTM_BT601 (0 << 9) +#define VI6_WPF_OUTFMT_WRTM_BT601_EXT (1 << 9) +#define VI6_WPF_OUTFMT_WRTM_BT709 (2 << 9) +#define VI6_WPF_OUTFMT_WRTM_BT709_EXT (3 << 9) +#define VI6_WPF_OUTFMT_WRTM_MASK (7 << 9) +#define VI6_WPF_OUTFMT_CSC (1 << 8) +#define VI6_WPF_OUTFMT_WRFMT_MASK (0x7f << 0) +#define VI6_WPF_OUTFMT_WRFMT_SHIFT 0 + +#define VI6_WPF_DSWAP 0x1010 +#define VI6_WPF_DSWAP_P_LLS (1 << 3) +#define VI6_WPF_DSWAP_P_LWS (1 << 2) +#define VI6_WPF_DSWAP_P_WDS (1 << 1) +#define VI6_WPF_DSWAP_P_BTS (1 << 0) + +#define VI6_WPF_RNDCTRL 0x1014 +#define VI6_WPF_RNDCTRL_CBRM (1 << 28) +#define VI6_WPF_RNDCTRL_ABRM_TRUNC (0 << 24) +#define VI6_WPF_RNDCTRL_ABRM_ROUND (1 << 24) +#define VI6_WPF_RNDCTRL_ABRM_THRESH (2 << 24) +#define VI6_WPF_RNDCTRL_ABRM_MASK (3 << 24) +#define VI6_WPF_RNDCTRL_ATHRESH_MASK (0xff << 16) +#define VI6_WPF_RNDCTRL_ATHRESH_SHIFT 16 +#define VI6_WPF_RNDCTRL_CLMD_FULL (0 << 12) +#define VI6_WPF_RNDCTRL_CLMD_CLIP (1 << 12) +#define VI6_WPF_RNDCTRL_CLMD_EXT (2 << 12) +#define VI6_WPF_RNDCTRL_CLMD_MASK (3 << 12) + +#define VI6_WPF_DSTM_STRIDE_Y 0x101c +#define VI6_WPF_DSTM_STRIDE_C 0x1020 +#define VI6_WPF_DSTM_ADDR_Y 0x1024 +#define VI6_WPF_DSTM_ADDR_C0 0x1028 +#define VI6_WPF_DSTM_ADDR_C1 0x102c + +#define VI6_WPF_WRBCK_CTRL 0x1034 +#define VI6_WPF_WRBCK_CTRL_WBMD (1 << 0) + +/* ----------------------------------------------------------------------------- + * DPR Control Registers + */ + +#define VI6_DPR_RPF_ROUTE(n) (0x2000 + (n) * 4) + +#define VI6_DPR_WPF_FPORCH(n) (0x2014 + (n) * 4) +#define VI6_DPR_WPF_FPORCH_FP_WPFN (5 << 8) + +#define VI6_DPR_SRU_ROUTE 0x2024 +#define VI6_DPR_UDS_ROUTE(n) (0x2028 + (n) * 4) +#define VI6_DPR_LUT_ROUTE 0x203c +#define VI6_DPR_CLU_ROUTE 0x2040 +#define VI6_DPR_HST_ROUTE 0x2044 +#define VI6_DPR_HSI_ROUTE 0x2048 +#define VI6_DPR_BRU_ROUTE 0x204c +#define VI6_DPR_ROUTE_FXA_MASK (0xff << 8) +#define VI6_DPR_ROUTE_FXA_SHIFT 16 +#define VI6_DPR_ROUTE_FP_MASK (0xff << 8) +#define VI6_DPR_ROUTE_FP_SHIFT 8 +#define VI6_DPR_ROUTE_RT_MASK (0x3f << 0) +#define VI6_DPR_ROUTE_RT_SHIFT 0 + +#define VI6_DPR_HGO_SMPPT 0x2050 +#define VI6_DPR_HGT_SMPPT 0x2054 +#define VI6_DPR_SMPPT_TGW_MASK (7 << 8) +#define VI6_DPR_SMPPT_TGW_SHIFT 8 +#define VI6_DPR_SMPPT_PT_MASK (0x3f << 0) +#define VI6_DPR_SMPPT_PT_SHIFT 0 + +#define VI6_DPR_NODE_RPF(n) (n) +#define VI6_DPR_NODE_SRU 16 +#define VI6_DPR_NODE_UDS(n) (17 + (n)) +#define VI6_DPR_NODE_LUT 22 +#define VI6_DPR_NODE_BRU_IN(n) (23 + (n)) +#define VI6_DPR_NODE_BRU_OUT 27 +#define VI6_DPR_NODE_CLU 29 +#define VI6_DPR_NODE_HST 30 +#define VI6_DPR_NODE_HSI 31 +#define VI6_DPR_NODE_LIF 55 +#define VI6_DPR_NODE_WPF(n) (56 + (n)) +#define VI6_DPR_NODE_UNUSED 63 + +/* ----------------------------------------------------------------------------- + * SRU Control Registers + */ + +#define VI6_SRU_CTRL0 0x2200 +#define VI6_SRU_CTRL1 0x2204 +#define VI6_SRU_CTRL2 0x2208 + +/* ----------------------------------------------------------------------------- + * UDS Control Registers + */ + +#define VI6_UDS_OFFSET 0x100 + +#define VI6_UDS_CTRL 0x2300 +#define VI6_UDS_CTRL_AMD (1 << 30) +#define VI6_UDS_CTRL_FMD (1 << 29) +#define VI6_UDS_CTRL_BLADV (1 << 28) +#define VI6_UDS_CTRL_AON (1 << 25) +#define VI6_UDS_CTRL_ATHON (1 << 24) +#define VI6_UDS_CTRL_BC (1 << 20) +#define VI6_UDS_CTRL_NE_A (1 << 19) +#define VI6_UDS_CTRL_NE_RCR (1 << 18) +#define VI6_UDS_CTRL_NE_GY (1 << 17) +#define VI6_UDS_CTRL_NE_BCB (1 << 16) +#define VI6_UDS_CTRL_TDIPC (1 << 1) + +#define VI6_UDS_SCALE 0x2304 +#define VI6_UDS_SCALE_HMANT_MASK (0xf << 28) +#define VI6_UDS_SCALE_HMANT_SHIFT 28 +#define VI6_UDS_SCALE_HFRAC_MASK (0xfff << 16) +#define VI6_UDS_SCALE_HFRAC_SHIFT 16 +#define VI6_UDS_SCALE_VMANT_MASK (0xf << 12) +#define VI6_UDS_SCALE_VMANT_SHIFT 12 +#define VI6_UDS_SCALE_VFRAC_MASK (0xfff << 0) +#define VI6_UDS_SCALE_VFRAC_SHIFT 0 + +#define VI6_UDS_ALPTH 0x2308 +#define VI6_UDS_ALPTH_TH1_MASK (0xff << 8) +#define VI6_UDS_ALPTH_TH1_SHIFT 8 +#define VI6_UDS_ALPTH_TH0_MASK (0xff << 0) +#define VI6_UDS_ALPTH_TH0_SHIFT 0 + +#define VI6_UDS_ALPVAL 0x230c +#define VI6_UDS_ALPVAL_VAL2_MASK (0xff << 16) +#define VI6_UDS_ALPVAL_VAL2_SHIFT 16 +#define VI6_UDS_ALPVAL_VAL1_MASK (0xff << 8) +#define VI6_UDS_ALPVAL_VAL1_SHIFT 8 +#define VI6_UDS_ALPVAL_VAL0_MASK (0xff << 0) +#define VI6_UDS_ALPVAL_VAL0_SHIFT 0 + +#define VI6_UDS_PASS_BWIDTH 0x2310 +#define VI6_UDS_PASS_BWIDTH_H_MASK (0x7f << 16) +#define VI6_UDS_PASS_BWIDTH_H_SHIFT 16 +#define VI6_UDS_PASS_BWIDTH_V_MASK (0x7f << 0) +#define VI6_UDS_PASS_BWIDTH_V_SHIFT 0 + +#define VI6_UDS_IPC 0x2318 +#define VI6_UDS_IPC_FIELD (1 << 27) +#define VI6_UDS_IPC_VEDP_MASK (0xfff << 0) +#define VI6_UDS_IPC_VEDP_SHIFT 0 + +#define VI6_UDS_CLIP_SIZE 0x2324 +#define VI6_UDS_CLIP_SIZE_HSIZE_MASK (0x1fff << 16) +#define VI6_UDS_CLIP_SIZE_HSIZE_SHIFT 16 +#define VI6_UDS_CLIP_SIZE_VSIZE_MASK (0x1fff << 0) +#define VI6_UDS_CLIP_SIZE_VSIZE_SHIFT 0 + +#define VI6_UDS_FILL_COLOR 0x2328 +#define VI6_UDS_FILL_COLOR_RFILC_MASK (0xff << 16) +#define VI6_UDS_FILL_COLOR_RFILC_SHIFT 16 +#define VI6_UDS_FILL_COLOR_GFILC_MASK (0xff << 8) +#define VI6_UDS_FILL_COLOR_GFILC_SHIFT 8 +#define VI6_UDS_FILL_COLOR_BFILC_MASK (0xff << 0) +#define VI6_UDS_FILL_COLOR_BFILC_SHIFT 0 + +/* ----------------------------------------------------------------------------- + * LUT Control Registers + */ + +#define VI6_LUT_CTRL 0x2800 + +/* ----------------------------------------------------------------------------- + * CLU Control Registers + */ + +#define VI6_CLU_CTRL 0x2900 + +/* ----------------------------------------------------------------------------- + * HST Control Registers + */ + +#define VI6_HST_CTRL 0x2a00 + +/* ----------------------------------------------------------------------------- + * HSI Control Registers + */ + +#define VI6_HSI_CTRL 0x2b00 + +/* ----------------------------------------------------------------------------- + * BRU Control Registers + */ + +#define VI6_BRU_INCTRL 0x2c00 +#define VI6_BRU_VIRRPF_SIZE 0x2c04 +#define VI6_BRU_VIRRPF_LOC 0x2c08 +#define VI6_BRU_VIRRPF_COL 0x2c0c +#define VI6_BRU_CTRL(n) (0x2c10 + (n) * 8) +#define VI6_BRU_BLD(n) (0x2c14 + (n) * 8) +#define VI6_BRU_ROP 0x2c30 + +/* ----------------------------------------------------------------------------- + * HGO Control Registers + */ + +#define VI6_HGO_OFFSET 0x3000 +#define VI6_HGO_SIZE 0x3004 +#define VI6_HGO_MODE 0x3008 +#define VI6_HGO_LB_TH 0x300c +#define VI6_HGO_LBn_H(n) (0x3010 + (n) * 8) +#define VI6_HGO_LBn_V(n) (0x3014 + (n) * 8) +#define VI6_HGO_R_HISTO 0x3030 +#define VI6_HGO_R_MAXMIN 0x3130 +#define VI6_HGO_R_SUM 0x3134 +#define VI6_HGO_R_LB_DET 0x3138 +#define VI6_HGO_G_HISTO 0x3140 +#define VI6_HGO_G_MAXMIN 0x3240 +#define VI6_HGO_G_SUM 0x3244 +#define VI6_HGO_G_LB_DET 0x3248 +#define VI6_HGO_B_HISTO 0x3250 +#define VI6_HGO_B_MAXMIN 0x3350 +#define VI6_HGO_B_SUM 0x3354 +#define VI6_HGO_B_LB_DET 0x3358 +#define VI6_HGO_REGRST 0x33fc + +/* ----------------------------------------------------------------------------- + * HGT Control Registers + */ + +#define VI6_HGT_OFFSET 0x3400 +#define VI6_HGT_SIZE 0x3404 +#define VI6_HGT_MODE 0x3408 +#define VI6_HGT_HUE_AREA(n) (0x340c + (n) * 4) +#define VI6_HGT_LB_TH 0x3424 +#define VI6_HGT_LBn_H(n) (0x3438 + (n) * 8) +#define VI6_HGT_LBn_V(n) (0x342c + (n) * 8) +#define VI6_HGT_HISTO(m, n) (0x3450 + (m) * 128 + (n) * 4) +#define VI6_HGT_MAXMIN 0x3750 +#define VI6_HGT_SUM 0x3754 +#define VI6_HGT_LB_DET 0x3758 +#define VI6_HGT_REGRST 0x37fc + +/* ----------------------------------------------------------------------------- + * LIF Control Registers + */ + +#define VI6_LIF_CTRL 0x3b00 +#define VI6_LIF_CTRL_OBTH_MASK (0x7ff << 16) +#define VI6_LIF_CTRL_OBTH_SHIFT 16 +#define VI6_LIF_CTRL_CFMT (1 << 4) +#define VI6_LIF_CTRL_REQSEL (1 << 1) +#define VI6_LIF_CTRL_LIF_EN (1 << 0) + +#define VI6_LIF_CSBTH 0x3b04 +#define VI6_LIF_CSBTH_HBTH_MASK (0x7ff << 16) +#define VI6_LIF_CSBTH_HBTH_SHIFT 16 +#define VI6_LIF_CSBTH_LBTH_MASK (0x7ff << 0) +#define VI6_LIF_CSBTH_LBTH_SHIFT 0 + +/* ----------------------------------------------------------------------------- + * Security Control Registers + */ + +#define VI6_SECURITY_CTRL0 0x3d00 +#define VI6_SECURITY_CTRL1 0x3d04 + +/* ----------------------------------------------------------------------------- + * RPF CLUT Registers + */ + +#define VI6_CLUT_TABLE 0x4000 + +/* ----------------------------------------------------------------------------- + * 1D LUT Registers + */ + +#define VI6_LUT_TABLE 0x7000 + +/* ----------------------------------------------------------------------------- + * 3D LUT Registers + */ + +#define VI6_CLU_ADDR 0x7400 +#define VI6_CLU_DATA 0x7404 + +/* ----------------------------------------------------------------------------- + * Formats + */ + +#define VI6_FMT_RGB_332 0x00 +#define VI6_FMT_XRGB_4444 0x01 +#define VI6_FMT_RGBX_4444 0x02 +#define VI6_FMT_XRGB_1555 0x04 +#define VI6_FMT_RGBX_5551 0x05 +#define VI6_FMT_RGB_565 0x06 +#define VI6_FMT_AXRGB_86666 0x07 +#define VI6_FMT_RGBXA_66668 0x08 +#define VI6_FMT_XRGBA_66668 0x09 +#define VI6_FMT_ARGBX_86666 0x0a +#define VI6_FMT_AXRXGXB_8262626 0x0b +#define VI6_FMT_XRXGXBA_2626268 0x0c +#define VI6_FMT_ARXGXBX_8626262 0x0d +#define VI6_FMT_RXGXBXA_6262628 0x0e +#define VI6_FMT_XRGB_6666 0x0f +#define VI6_FMT_RGBX_6666 0x10 +#define VI6_FMT_XRXGXB_262626 0x11 +#define VI6_FMT_RXGXBX_626262 0x12 +#define VI6_FMT_ARGB_8888 0x13 +#define VI6_FMT_RGBA_8888 0x14 +#define VI6_FMT_RGB_888 0x15 +#define VI6_FMT_XRGXGB_763763 0x16 +#define VI6_FMT_XXRGB_86666 0x17 +#define VI6_FMT_BGR_888 0x18 +#define VI6_FMT_ARGB_4444 0x19 +#define VI6_FMT_RGBA_4444 0x1a +#define VI6_FMT_ARGB_1555 0x1b +#define VI6_FMT_RGBA_5551 0x1c +#define VI6_FMT_ABGR_4444 0x1d +#define VI6_FMT_BGRA_4444 0x1e +#define VI6_FMT_ABGR_1555 0x1f +#define VI6_FMT_BGRA_5551 0x20 +#define VI6_FMT_XBXGXR_262626 0x21 +#define VI6_FMT_ABGR_8888 0x22 +#define VI6_FMT_XXRGB_88565 0x23 + +#define VI6_FMT_Y_UV_444 0x40 +#define VI6_FMT_Y_UV_422 0x41 +#define VI6_FMT_Y_UV_420 0x42 +#define VI6_FMT_YUV_444 0x46 +#define VI6_FMT_YUYV_422 0x47 +#define VI6_FMT_YYUV_422 0x48 +#define VI6_FMT_YUV_420 0x49 +#define VI6_FMT_Y_U_V_444 0x4a +#define VI6_FMT_Y_U_V_422 0x4b +#define VI6_FMT_Y_U_V_420 0x4c + +#endif /* __VSP1_REGS_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c new file mode 100644 index 000000000000..254871d3423e --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -0,0 +1,209 @@ +/* + * vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include + +#include + +#include "vsp1.h" +#include "vsp1_rwpf.h" +#include "vsp1_video.h" + +#define RPF_MAX_WIDTH 8190 +#define RPF_MAX_HEIGHT 8190 + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_rpf_read(struct vsp1_rwpf *rpf, u32 reg) +{ + return vsp1_read(rpf->entity.vsp1, + reg + rpf->entity.index * VI6_RPF_OFFSET); +} + +static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data) +{ + vsp1_write(rpf->entity.vsp1, + reg + rpf->entity.index * VI6_RPF_OFFSET, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct vsp1_rwpf *rpf = to_rwpf(subdev); + const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo; + const struct v4l2_pix_format_mplane *format = &rpf->video.format; + u32 pstride; + u32 infmt; + + if (!enable) + return 0; + + /* Source size and stride. Cropping isn't supported yet. */ + vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE, + (format->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | + (format->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); + vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE, + (format->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | + (format->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); + + pstride = format->plane_fmt[0].bytesperline + << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; + if (format->num_planes > 1) + pstride |= format->plane_fmt[1].bytesperline + << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; + + vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride); + + /* Format */ + infmt = VI6_RPF_INFMT_CIPM + | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); + + if (fmtinfo->swap_yc) + infmt |= VI6_RPF_INFMT_SPYCS; + if (fmtinfo->swap_uv) + infmt |= VI6_RPF_INFMT_SPUVS; + + if (rpf->entity.formats[RWPF_PAD_SINK].code != + rpf->entity.formats[RWPF_PAD_SOURCE].code) + infmt |= VI6_RPF_INFMT_CSC; + + vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt); + vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap); + + /* Output location. Composing isn't supported yet. */ + vsp1_rpf_write(rpf, VI6_RPF_LOC, 0); + + /* Disable alpha, mask and color key. Set the alpha channel to a fixed + * value of 255. + */ + vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_ASEL_FIXED); + vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET, + 255 << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); + vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0); + vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops rpf_video_ops = { + .s_stream = rpf_s_stream, +}; + +static struct v4l2_subdev_pad_ops rpf_pad_ops = { + .enum_mbus_code = vsp1_rwpf_enum_mbus_code, + .enum_frame_size = vsp1_rwpf_enum_frame_size, + .get_fmt = vsp1_rwpf_get_format, + .set_fmt = vsp1_rwpf_set_format, +}; + +static struct v4l2_subdev_ops rpf_ops = { + .video = &rpf_video_ops, + .pad = &rpf_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Video Device Operations + */ + +static void rpf_vdev_queue(struct vsp1_video *video, + struct vsp1_video_buffer *buf) +{ + struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video); + + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, buf->addr[0]); + if (buf->buf.num_planes > 1) + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, buf->addr[1]); + if (buf->buf.num_planes > 2) + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, buf->addr[2]); +} + +static const struct vsp1_video_operations rpf_vdev_ops = { + .queue = rpf_vdev_queue, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index) +{ + struct v4l2_subdev *subdev; + struct vsp1_video *video; + struct vsp1_rwpf *rpf; + int ret; + + rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL); + if (rpf == NULL) + return ERR_PTR(-ENOMEM); + + rpf->max_width = RPF_MAX_WIDTH; + rpf->max_height = RPF_MAX_HEIGHT; + + rpf->entity.type = VSP1_ENTITY_RPF; + rpf->entity.index = index; + rpf->entity.id = VI6_DPR_NODE_RPF(index); + + ret = vsp1_entity_init(vsp1, &rpf->entity, 2); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the V4L2 subdev. */ + subdev = &rpf->entity.subdev; + v4l2_subdev_init(subdev, &rpf_ops); + + subdev->entity.ops = &vsp1_media_ops; + subdev->internal_ops = &vsp1_subdev_internal_ops; + snprintf(subdev->name, sizeof(subdev->name), "%s rpf.%u", + dev_name(vsp1->dev), index); + v4l2_set_subdevdata(subdev, rpf); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + vsp1_entity_init_formats(subdev, NULL); + + /* Initialize the video device. */ + video = &rpf->video; + + video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + video->vsp1 = vsp1; + video->ops = &rpf_vdev_ops; + + ret = vsp1_video_init(video, &rpf->entity); + if (ret < 0) + goto error_video; + + /* Connect the video device to the RPF. */ + ret = media_entity_create_link(&rpf->video.video.entity, 0, + &rpf->entity.subdev.entity, + RWPF_PAD_SINK, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret < 0) + goto error_link; + + return rpf; + +error_link: + vsp1_video_cleanup(video); +error_video: + media_entity_cleanup(&rpf->entity.subdev.entity); + return ERR_PTR(ret); +} diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c new file mode 100644 index 000000000000..9752d5516ceb --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -0,0 +1,124 @@ +/* + * vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include + +#include "vsp1.h" +#include "vsp1_rwpf.h" +#include "vsp1_video.h" + +#define RWPF_MIN_WIDTH 1 +#define RWPF_MIN_HEIGHT 1 + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const unsigned int codes[] = { + V4L2_MBUS_FMT_ARGB8888_1X32, + V4L2_MBUS_FMT_AYUV8_1X32, + }; + + if (code->index >= ARRAY_SIZE(codes)) + return -EINVAL; + + code->code = codes[code->index]; + + return 0; +} + +int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vsp1_rwpf *rwpf = to_rwpf(subdev); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(fh, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == RWPF_PAD_SINK) { + fse->min_width = RWPF_MIN_WIDTH; + fse->max_width = rwpf->max_width; + fse->min_height = RWPF_MIN_HEIGHT; + fse->max_height = rwpf->max_height; + } else { + /* The size on the source pad are fixed and always identical to + * the size on the sink pad. + */ + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} + +int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_rwpf *rwpf = to_rwpf(subdev); + + fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad, + fmt->which); + + return 0; +} + +int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_rwpf *rwpf = to_rwpf(subdev); + struct v4l2_mbus_framefmt *format; + + /* Default to YUV if the requested format is not supported. */ + if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 && + fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32) + fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32; + + format = vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad, + fmt->which); + + if (fmt->pad == RWPF_PAD_SOURCE) { + /* The RWPF performs format conversion but can't scale, only the + * format code can be changed on the source pad. + */ + format->code = fmt->format.code; + fmt->format = *format; + return 0; + } + + format->code = fmt->format.code; + format->width = clamp_t(unsigned int, fmt->format.width, + RWPF_MIN_WIDTH, rwpf->max_width); + format->height = clamp_t(unsigned int, fmt->format.height, + RWPF_MIN_HEIGHT, rwpf->max_height); + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE, + fmt->which); + *format = fmt->format; + + return 0; +} diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h new file mode 100644 index 000000000000..c182d85f36b3 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -0,0 +1,53 @@ +/* + * vsp1_rwpf.h -- R-Car VSP1 Read and Write Pixel Formatters + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_RWPF_H__ +#define __VSP1_RWPF_H__ + +#include +#include + +#include "vsp1.h" +#include "vsp1_entity.h" +#include "vsp1_video.h" + +#define RWPF_PAD_SINK 0 +#define RWPF_PAD_SOURCE 1 + +struct vsp1_rwpf { + struct vsp1_entity entity; + struct vsp1_video video; + + unsigned int max_width; + unsigned int max_height; +}; + +static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_rwpf, entity.subdev); +} + +struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index); +struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index); + +int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code); +int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse); +int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt); +int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt); + +#endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c new file mode 100644 index 000000000000..0e50b37f060d --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -0,0 +1,346 @@ +/* + * vsp1_uds.c -- R-Car VSP1 Up and Down Scaler + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include + +#include "vsp1.h" +#include "vsp1_uds.h" + +#define UDS_MIN_SIZE 4U +#define UDS_MAX_SIZE 8190U + +#define UDS_MIN_FACTOR 0x0100 +#define UDS_MAX_FACTOR 0xffff + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_uds_read(struct vsp1_uds *uds, u32 reg) +{ + return vsp1_read(uds->entity.vsp1, + reg + uds->entity.index * VI6_UDS_OFFSET); +} + +static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data) +{ + vsp1_write(uds->entity.vsp1, + reg + uds->entity.index * VI6_UDS_OFFSET, data); +} + +/* ----------------------------------------------------------------------------- + * Scaling Computation + */ + +/* + * uds_output_size - Return the output size for an input size and scaling ratio + * @input: input size in pixels + * @ratio: scaling ratio in U4.12 fixed-point format + */ +static unsigned int uds_output_size(unsigned int input, unsigned int ratio) +{ + if (ratio > 4096) { + /* Down-scaling */ + unsigned int mp; + + mp = ratio / 4096; + mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4); + + return (input - 1) / mp * mp * 4096 / ratio + 1; + } else { + /* Up-scaling */ + return (input - 1) * 4096 / ratio + 1; + } +} + +/* + * uds_output_limits - Return the min and max output sizes for an input size + * @input: input size in pixels + * @minimum: minimum output size (returned) + * @maximum: maximum output size (returned) + */ +static void uds_output_limits(unsigned int input, + unsigned int *minimum, unsigned int *maximum) +{ + *minimum = max(uds_output_size(input, UDS_MAX_FACTOR), UDS_MIN_SIZE); + *maximum = min(uds_output_size(input, UDS_MIN_FACTOR), UDS_MAX_SIZE); +} + +/* + * uds_passband_width - Return the passband filter width for a scaling ratio + * @ratio: scaling ratio in U4.12 fixed-point format + */ +static unsigned int uds_passband_width(unsigned int ratio) +{ + if (ratio >= 4096) { + /* Down-scaling */ + unsigned int mp; + + mp = ratio / 4096; + mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4); + + return 64 * 4096 * mp / ratio; + } else { + /* Up-scaling */ + return 64; + } +} + +static unsigned int uds_compute_ratio(unsigned int input, unsigned int output) +{ + /* TODO: This is an approximation that will need to be refined. */ + return (input - 1) * 4096 / (output - 1); +} + +static void uds_compute_ratios(struct vsp1_uds *uds) +{ + struct v4l2_mbus_framefmt *input = &uds->entity.formats[UDS_PAD_SINK]; + struct v4l2_mbus_framefmt *output = + &uds->entity.formats[UDS_PAD_SOURCE]; + + uds->hscale = uds_compute_ratio(input->width, output->width); + uds->vscale = uds_compute_ratio(input->height, output->height); + + dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", + uds->hscale, uds->vscale); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static int uds_s_stream(struct v4l2_subdev *subdev, int enable) +{ + const struct v4l2_mbus_framefmt *format; + struct vsp1_uds *uds = to_uds(subdev); + + if (!enable) + return 0; + + /* Enable multi-tap scaling. */ + vsp1_uds_write(uds, VI6_UDS_CTRL, VI6_UDS_CTRL_BC); + + vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH, + (uds_passband_width(uds->hscale) + << VI6_UDS_PASS_BWIDTH_H_SHIFT) | + (uds_passband_width(uds->vscale) + << VI6_UDS_PASS_BWIDTH_V_SHIFT)); + + + /* Set the scaling ratios and the output size. */ + format = &uds->entity.formats[UDS_PAD_SOURCE]; + + vsp1_uds_write(uds, VI6_UDS_SCALE, + (uds->hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | + (uds->vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); + vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE, + (format->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | + (format->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static int uds_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + static const unsigned int codes[] = { + V4L2_MBUS_FMT_ARGB8888_1X32, + V4L2_MBUS_FMT_AYUV8_1X32, + }; + + if (code->pad == UDS_PAD_SINK) { + if (code->index >= ARRAY_SIZE(codes)) + return -EINVAL; + + code->code = codes[code->index]; + } else { + struct v4l2_mbus_framefmt *format; + + /* The UDS can't perform format conversion, the sink format is + * always identical to the source format. + */ + if (code->index) + return -EINVAL; + + format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK); + code->code = format->code; + } + + return 0; +} + +static int uds_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == UDS_PAD_SINK) { + fse->min_width = UDS_MIN_SIZE; + fse->max_width = UDS_MAX_SIZE; + fse->min_height = UDS_MIN_SIZE; + fse->max_height = UDS_MAX_SIZE; + } else { + uds_output_limits(format->width, &fse->min_width, + &fse->max_width); + uds_output_limits(format->height, &fse->min_height, + &fse->max_height); + } + + return 0; +} + +static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_uds *uds = to_uds(subdev); + + fmt->format = *vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad, + fmt->which); + + return 0; +} + +static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_fh *fh, + unsigned int pad, struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_mbus_framefmt *format; + unsigned int minimum; + unsigned int maximum; + + switch (pad) { + case UDS_PAD_SINK: + /* Default to YUV if the requested format is not supported. */ + if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 && + fmt->code != V4L2_MBUS_FMT_AYUV8_1X32) + fmt->code = V4L2_MBUS_FMT_AYUV8_1X32; + + fmt->width = clamp(fmt->width, UDS_MIN_SIZE, UDS_MAX_SIZE); + fmt->height = clamp(fmt->height, UDS_MIN_SIZE, UDS_MAX_SIZE); + break; + + case UDS_PAD_SOURCE: + /* The UDS scales but can't perform format conversion. */ + format = vsp1_entity_get_pad_format(&uds->entity, fh, + UDS_PAD_SINK, which); + fmt->code = format->code; + + uds_output_limits(format->width, &minimum, &maximum); + fmt->width = clamp(fmt->width, minimum, maximum); + uds_output_limits(format->height, &minimum, &maximum); + fmt->height = clamp(fmt->height, minimum, maximum); + break; + } + + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vsp1_uds *uds = to_uds(subdev); + struct v4l2_mbus_framefmt *format; + + uds_try_format(uds, fh, fmt->pad, &fmt->format, fmt->which); + + format = vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad, + fmt->which); + *format = fmt->format; + + if (fmt->pad == UDS_PAD_SINK) { + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(&uds->entity, fh, + UDS_PAD_SOURCE, fmt->which); + *format = fmt->format; + + uds_try_format(uds, fh, UDS_PAD_SOURCE, format, fmt->which); + } + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + uds_compute_ratios(uds); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops uds_video_ops = { + .s_stream = uds_s_stream, +}; + +static struct v4l2_subdev_pad_ops uds_pad_ops = { + .enum_mbus_code = uds_enum_mbus_code, + .enum_frame_size = uds_enum_frame_size, + .get_fmt = uds_get_format, + .set_fmt = uds_set_format, +}; + +static struct v4l2_subdev_ops uds_ops = { + .video = &uds_video_ops, + .pad = &uds_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index) +{ + struct v4l2_subdev *subdev; + struct vsp1_uds *uds; + int ret; + + uds = devm_kzalloc(vsp1->dev, sizeof(*uds), GFP_KERNEL); + if (uds == NULL) + return ERR_PTR(-ENOMEM); + + uds->entity.type = VSP1_ENTITY_UDS; + uds->entity.index = index; + uds->entity.id = VI6_DPR_NODE_UDS(index); + + ret = vsp1_entity_init(vsp1, &uds->entity, 2); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the V4L2 subdev. */ + subdev = &uds->entity.subdev; + v4l2_subdev_init(subdev, &uds_ops); + + subdev->entity.ops = &vsp1_media_ops; + subdev->internal_ops = &vsp1_subdev_internal_ops; + snprintf(subdev->name, sizeof(subdev->name), "%s uds.%u", + dev_name(vsp1->dev), index); + v4l2_set_subdevdata(subdev, uds); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + vsp1_entity_init_formats(subdev, NULL); + + return uds; +} diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h new file mode 100644 index 000000000000..972a285abdb9 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_uds.h @@ -0,0 +1,40 @@ +/* + * vsp1_uds.h -- R-Car VSP1 Up and Down Scaler + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_UDS_H__ +#define __VSP1_UDS_H__ + +#include +#include + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define UDS_PAD_SINK 0 +#define UDS_PAD_SOURCE 1 + +struct vsp1_uds { + struct vsp1_entity entity; + + unsigned int hscale; + unsigned int vscale; +}; + +static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_uds, entity.subdev); +} + +struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index); + +#endif /* __VSP1_UDS_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c new file mode 100644 index 000000000000..f51f84232e2f --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -0,0 +1,1071 @@ +/* + * vsp1_video.c -- R-Car VSP1 Video Node + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "vsp1.h" +#include "vsp1_entity.h" +#include "vsp1_rwpf.h" +#include "vsp1_video.h" + +#define VSP1_VIDEO_DEF_FORMAT V4L2_PIX_FMT_YUYV +#define VSP1_VIDEO_DEF_WIDTH 1024 +#define VSP1_VIDEO_DEF_HEIGHT 768 + +#define VSP1_VIDEO_MIN_WIDTH 2U +#define VSP1_VIDEO_MAX_WIDTH 8190U +#define VSP1_VIDEO_MIN_HEIGHT 2U +#define VSP1_VIDEO_MAX_HEIGHT 8190U + +/* ----------------------------------------------------------------------------- + * Helper functions + */ + +static const struct vsp1_format_info vsp1_video_formats[] = { + { V4L2_PIX_FMT_RGB332, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 8, 0, 0 }, false, false, 1, 1 }, + { V4L2_PIX_FMT_RGB444, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1 }, + { V4L2_PIX_FMT_RGB555, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1 }, + { V4L2_PIX_FMT_RGB565, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_RGB_565, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1 }, + { V4L2_PIX_FMT_BGR24, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_BGR_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 24, 0, 0 }, false, false, 1, 1 }, + { V4L2_PIX_FMT_RGB24, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 24, 0, 0 }, false, false, 1, 1 }, + { V4L2_PIX_FMT_BGR32, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, + 1, { 32, 0, 0 }, false, false, 1, 1 }, + { V4L2_PIX_FMT_RGB32, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 32, 0, 0 }, false, false, 1, 1 }, + { V4L2_PIX_FMT_UYVY, V4L2_MBUS_FMT_AYUV8_1X32, + VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 16, 0, 0 }, false, false, 2, 1 }, + { V4L2_PIX_FMT_VYUY, V4L2_MBUS_FMT_AYUV8_1X32, + VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 16, 0, 0 }, false, true, 2, 1 }, + { V4L2_PIX_FMT_YUYV, V4L2_MBUS_FMT_AYUV8_1X32, + VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 16, 0, 0 }, true, false, 2, 1 }, + { V4L2_PIX_FMT_YVYU, V4L2_MBUS_FMT_AYUV8_1X32, + VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 16, 0, 0 }, true, true, 2, 1 }, + { V4L2_PIX_FMT_NV12M, V4L2_MBUS_FMT_AYUV8_1X32, + VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 2, { 8, 16, 0 }, false, false, 2, 2 }, + { V4L2_PIX_FMT_NV21M, V4L2_MBUS_FMT_AYUV8_1X32, + VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 2, { 8, 16, 0 }, false, true, 2, 2 }, + { V4L2_PIX_FMT_NV16M, V4L2_MBUS_FMT_AYUV8_1X32, + VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 2, { 8, 16, 0 }, false, false, 2, 1 }, + { V4L2_PIX_FMT_NV61M, V4L2_MBUS_FMT_AYUV8_1X32, + VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 2, { 8, 16, 0 }, false, true, 2, 1 }, + { V4L2_PIX_FMT_YUV420M, V4L2_MBUS_FMT_AYUV8_1X32, + VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 3, { 8, 8, 8 }, false, false, 2, 2 }, +}; + +/* + * vsp1_get_format_info - Retrieve format information for a 4CC + * @fourcc: the format 4CC + * + * Return a pointer to the format information structure corresponding to the + * given V4L2 format 4CC, or NULL if no corresponding format can be found. + */ +static const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { + const struct vsp1_format_info *info = &vsp1_video_formats[i]; + + if (info->fourcc == fourcc) + return info; + } + + return NULL; +} + + +static struct v4l2_subdev * +vsp1_video_remote_subdev(struct media_pad *local, u32 *pad) +{ + struct media_pad *remote; + + remote = media_entity_remote_pad(local); + if (remote == NULL || + media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +static int vsp1_video_verify_format(struct vsp1_video *video) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + int ret; + + subdev = vsp1_video_remote_subdev(&video->pad, &fmt.pad); + if (subdev == NULL) + return -EINVAL; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + if (video->fmtinfo->mbus != fmt.format.code || + video->format.height != fmt.format.height || + video->format.width != fmt.format.width) + return -EINVAL; + + return 0; +} + +static int __vsp1_video_try_format(struct vsp1_video *video, + struct v4l2_pix_format_mplane *pix, + const struct vsp1_format_info **fmtinfo) +{ + const struct vsp1_format_info *info; + unsigned int width = pix->width; + unsigned int height = pix->height; + unsigned int i; + + /* Retrieve format information and select the default format if the + * requested format isn't supported. + */ + info = vsp1_get_format_info(pix->pixelformat); + if (info == NULL) + info = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT); + + pix->pixelformat = info->fourcc; + pix->colorspace = V4L2_COLORSPACE_SRGB; + pix->field = V4L2_FIELD_NONE; + memset(pix->reserved, 0, sizeof(pix->reserved)); + + /* Align the width and height for YUV 4:2:2 and 4:2:0 formats. */ + width = round_down(width, info->hsub); + height = round_down(height, info->vsub); + + /* Clamp the width and height. */ + pix->width = clamp(width, VSP1_VIDEO_MIN_WIDTH, VSP1_VIDEO_MAX_WIDTH); + pix->height = clamp(height, VSP1_VIDEO_MIN_HEIGHT, + VSP1_VIDEO_MAX_HEIGHT); + + /* Compute and clamp the stride and image size. While not documented in + * the datasheet, strides not aligned to a multiple of 128 bytes result + * in image corruption. + */ + for (i = 0; i < max(info->planes, 2U); ++i) { + unsigned int hsub = i > 0 ? info->hsub : 1; + unsigned int vsub = i > 0 ? info->vsub : 1; + unsigned int align = 128; + unsigned int bpl; + + bpl = clamp_t(unsigned int, pix->plane_fmt[i].bytesperline, + pix->width / hsub * info->bpp[i] / 8, + round_down(65535U, align)); + + pix->plane_fmt[i].bytesperline = round_up(bpl, align); + pix->plane_fmt[i].sizeimage = pix->plane_fmt[i].bytesperline + * pix->height / vsub; + } + + if (info->planes == 3) { + /* The second and third planes must have the same stride. */ + pix->plane_fmt[2].bytesperline = pix->plane_fmt[1].bytesperline; + pix->plane_fmt[2].sizeimage = pix->plane_fmt[1].sizeimage; + } + + pix->num_planes = info->planes; + + if (fmtinfo) + *fmtinfo = info; + + return 0; +} + +static bool +vsp1_video_format_adjust(struct vsp1_video *video, + const struct v4l2_pix_format_mplane *format, + struct v4l2_pix_format_mplane *adjust) +{ + unsigned int i; + + *adjust = *format; + __vsp1_video_try_format(video, adjust, NULL); + + if (format->width != adjust->width || + format->height != adjust->height || + format->pixelformat != adjust->pixelformat || + format->num_planes != adjust->num_planes) + return false; + + for (i = 0; i < format->num_planes; ++i) { + if (format->plane_fmt[i].bytesperline != + adjust->plane_fmt[i].bytesperline) + return false; + + adjust->plane_fmt[i].sizeimage = + max(adjust->plane_fmt[i].sizeimage, + format->plane_fmt[i].sizeimage); + } + + return true; +} + +/* ----------------------------------------------------------------------------- + * Pipeline Management + */ + +static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, + struct vsp1_rwpf *output) +{ + struct vsp1_entity *entity; + unsigned int entities = 0; + struct media_pad *pad; + bool uds_found = false; + + pad = media_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]); + + while (1) { + if (pad == NULL) + return -EPIPE; + + /* We've reached a video node, that shouldn't have happened. */ + if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + return -EPIPE; + + entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); + + /* We've reached the WPF, we're done. */ + if (entity->type == VSP1_ENTITY_WPF) + break; + + /* Ensure the branch has no loop. */ + if (entities & (1 << entity->subdev.entity.id)) + return -EPIPE; + + entities |= 1 << entity->subdev.entity.id; + + /* UDS can't be chained. */ + if (entity->type == VSP1_ENTITY_UDS) { + if (uds_found) + return -EPIPE; + uds_found = true; + } + + /* Follow the source link. The link setup operations ensure + * that the output fan-out can't be more than one, there is thus + * no need to verify here that only a single source link is + * activated. + */ + pad = &entity->pads[entity->source_pad]; + pad = media_entity_remote_pad(pad); + } + + /* The last entity must be the output WPF. */ + if (entity != &output->entity) + return -EPIPE; + + return 0; +} + +static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, + struct vsp1_video *video) +{ + struct media_entity_graph graph; + struct media_entity *entity = &video->video.entity; + struct media_device *mdev = entity->parent; + unsigned int i; + int ret; + + mutex_lock(&mdev->graph_mutex); + + /* Walk the graph to locate the entities and video nodes. */ + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + struct v4l2_subdev *subdev; + struct vsp1_rwpf *rwpf; + struct vsp1_entity *e; + + if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV) { + pipe->num_video++; + continue; + } + + subdev = media_entity_to_v4l2_subdev(entity); + e = to_vsp1_entity(subdev); + list_add_tail(&e->list_pipe, &pipe->entities); + + if (e->type == VSP1_ENTITY_RPF) { + rwpf = to_rwpf(subdev); + pipe->inputs[pipe->num_inputs++] = rwpf; + rwpf->video.pipe_index = pipe->num_inputs; + } else if (e->type == VSP1_ENTITY_WPF) { + rwpf = to_rwpf(subdev); + pipe->output = to_rwpf(subdev); + rwpf->video.pipe_index = 0; + } else if (e->type == VSP1_ENTITY_LIF) { + pipe->lif = e; + } + } + + mutex_unlock(&mdev->graph_mutex); + + /* We need one output and at least one input. */ + if (pipe->num_inputs == 0 || !pipe->output) { + ret = -EPIPE; + goto error; + } + + /* Follow links downstream for each input and make sure the graph + * contains no loop and that all branches end at the output WPF. + */ + for (i = 0; i < pipe->num_inputs; ++i) { + ret = vsp1_pipeline_validate_branch(pipe->inputs[i], + pipe->output); + if (ret < 0) + goto error; + } + + return 0; + +error: + INIT_LIST_HEAD(&pipe->entities); + pipe->buffers_ready = 0; + pipe->num_video = 0; + pipe->num_inputs = 0; + pipe->output = NULL; + pipe->lif = NULL; + return ret; +} + +static int vsp1_pipeline_init(struct vsp1_pipeline *pipe, + struct vsp1_video *video) +{ + int ret; + + mutex_lock(&pipe->lock); + + /* If we're the first user validate and initialize the pipeline. */ + if (pipe->use_count == 0) { + ret = vsp1_pipeline_validate(pipe, video); + if (ret < 0) + goto done; + } + + pipe->use_count++; + ret = 0; + +done: + mutex_unlock(&pipe->lock); + return ret; +} + +static void vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe) +{ + mutex_lock(&pipe->lock); + + /* If we're the last user clean up the pipeline. */ + if (--pipe->use_count == 0) { + INIT_LIST_HEAD(&pipe->entities); + pipe->state = VSP1_PIPELINE_STOPPED; + pipe->buffers_ready = 0; + pipe->num_video = 0; + pipe->num_inputs = 0; + pipe->output = NULL; + pipe->lif = NULL; + } + + mutex_unlock(&pipe->lock); +} + +static void vsp1_pipeline_run(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + + vsp1_write(vsp1, VI6_CMD(pipe->output->entity.index), VI6_CMD_STRCMD); + pipe->state = VSP1_PIPELINE_RUNNING; + pipe->buffers_ready = 0; +} + +static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) +{ + struct vsp1_entity *entity; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pipe->irqlock, flags); + pipe->state = VSP1_PIPELINE_STOPPING; + spin_unlock_irqrestore(&pipe->irqlock, flags); + + ret = wait_event_timeout(pipe->wq, pipe->state == VSP1_PIPELINE_STOPPED, + msecs_to_jiffies(500)); + ret = ret == 0 ? -ETIMEDOUT : 0; + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + if (entity->route) + vsp1_write(entity->vsp1, entity->route, + VI6_DPR_NODE_UNUSED); + + v4l2_subdev_call(&entity->subdev, video, s_stream, 0); + } + + return ret; +} + +static bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe) +{ + unsigned int mask; + + mask = ((1 << pipe->num_inputs) - 1) << 1; + if (!pipe->lif) + mask |= 1 << 0; + + return pipe->buffers_ready == mask; +} + +/* + * vsp1_video_complete_buffer - Complete the current buffer + * @video: the video node + * + * This function completes the current buffer by filling its sequence number, + * time stamp and payload size, and hands it back to the videobuf core. + * + * Return the next queued buffer or NULL if the queue is empty. + */ +static struct vsp1_video_buffer * +vsp1_video_complete_buffer(struct vsp1_video *video) +{ + struct vsp1_video_buffer *next = NULL; + struct vsp1_video_buffer *done; + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&video->irqlock, flags); + + if (list_empty(&video->irqqueue)) { + spin_unlock_irqrestore(&video->irqlock, flags); + return NULL; + } + + done = list_first_entry(&video->irqqueue, + struct vsp1_video_buffer, queue); + list_del(&done->queue); + + if (!list_empty(&video->irqqueue)) + next = list_first_entry(&video->irqqueue, + struct vsp1_video_buffer, queue); + + spin_unlock_irqrestore(&video->irqlock, flags); + + done->buf.v4l2_buf.sequence = video->sequence++; + v4l2_get_timestamp(&done->buf.v4l2_buf.timestamp); + for (i = 0; i < done->buf.num_planes; ++i) + vb2_set_plane_payload(&done->buf, i, done->length[i]); + vb2_buffer_done(&done->buf, VB2_BUF_STATE_DONE); + + return next; +} + +static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, + struct vsp1_video *video) +{ + struct vsp1_video_buffer *buf; + unsigned long flags; + + buf = vsp1_video_complete_buffer(video); + if (buf == NULL) + return; + + spin_lock_irqsave(&pipe->irqlock, flags); + + video->ops->queue(video, buf); + pipe->buffers_ready |= 1 << video->pipe_index; + + spin_unlock_irqrestore(&pipe->irqlock, flags); +} + +void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) +{ + unsigned long flags; + unsigned int i; + + if (pipe == NULL) + return; + + /* Complete buffers on all video nodes. */ + for (i = 0; i < pipe->num_inputs; ++i) + vsp1_video_frame_end(pipe, &pipe->inputs[i]->video); + + if (!pipe->lif) + vsp1_video_frame_end(pipe, &pipe->output->video); + + spin_lock_irqsave(&pipe->irqlock, flags); + + /* If a stop has been requested, mark the pipeline as stopped and + * return. + */ + if (pipe->state == VSP1_PIPELINE_STOPPING) { + pipe->state = VSP1_PIPELINE_STOPPED; + wake_up(&pipe->wq); + goto done; + } + + /* Restart the pipeline if ready. */ + if (vsp1_pipeline_ready(pipe)) + vsp1_pipeline_run(pipe); + +done: + spin_unlock_irqrestore(&pipe->irqlock, flags); +} + +/* ----------------------------------------------------------------------------- + * videobuf2 Queue Operations + */ + +static int +vsp1_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct vsp1_video *video = vb2_get_drv_priv(vq); + const struct v4l2_pix_format_mplane *format; + struct v4l2_pix_format_mplane pix_mp; + unsigned int i; + + if (fmt) { + /* Make sure the format is valid and adjust the sizeimage field + * if needed. + */ + if (!vsp1_video_format_adjust(video, &fmt->fmt.pix_mp, &pix_mp)) + return -EINVAL; + + format = &pix_mp; + } else { + format = &video->format; + } + + *nplanes = format->num_planes; + + for (i = 0; i < format->num_planes; ++i) { + sizes[i] = format->plane_fmt[i].sizeimage; + alloc_ctxs[i] = video->alloc_ctx; + } + + return 0; +} + +static int vsp1_video_buffer_prepare(struct vb2_buffer *vb) +{ + struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); + struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vb); + const struct v4l2_pix_format_mplane *format = &video->format; + unsigned int i; + + if (vb->num_planes < format->num_planes) + return -EINVAL; + + buf->video = video; + + for (i = 0; i < vb->num_planes; ++i) { + buf->addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); + buf->length[i] = vb2_plane_size(vb, i); + + if (buf->length[i] < format->plane_fmt[i].sizeimage) + return -EINVAL; + } + + return 0; +} + +static void vsp1_video_buffer_queue(struct vb2_buffer *vb) +{ + struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); + struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vb); + unsigned long flags; + bool empty; + + spin_lock_irqsave(&video->irqlock, flags); + empty = list_empty(&video->irqqueue); + list_add_tail(&buf->queue, &video->irqqueue); + spin_unlock_irqrestore(&video->irqlock, flags); + + if (!empty) + return; + + spin_lock_irqsave(&pipe->irqlock, flags); + + video->ops->queue(video, buf); + pipe->buffers_ready |= 1 << video->pipe_index; + + if (vb2_is_streaming(&video->queue) && + vsp1_pipeline_ready(pipe)) + vsp1_pipeline_run(pipe); + + spin_unlock_irqrestore(&pipe->irqlock, flags); +} + +static void vsp1_entity_route_setup(struct vsp1_entity *source) +{ + struct vsp1_entity *sink; + + if (source->route == 0) + return; + + sink = container_of(source->sink, struct vsp1_entity, subdev.entity); + vsp1_write(source->vsp1, source->route, sink->id); +} + +static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vsp1_video *video = vb2_get_drv_priv(vq); + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); + struct vsp1_entity *entity; + unsigned long flags; + int ret; + + mutex_lock(&pipe->lock); + if (pipe->stream_count == pipe->num_video - 1) { + list_for_each_entry(entity, &pipe->entities, list_pipe) { + vsp1_entity_route_setup(entity); + + ret = v4l2_subdev_call(&entity->subdev, video, + s_stream, 1); + if (ret < 0) { + mutex_unlock(&pipe->lock); + return ret; + } + } + } + + pipe->stream_count++; + mutex_unlock(&pipe->lock); + + spin_lock_irqsave(&pipe->irqlock, flags); + if (vsp1_pipeline_ready(pipe)) + vsp1_pipeline_run(pipe); + spin_unlock_irqrestore(&pipe->irqlock, flags); + + return 0; +} + +static int vsp1_video_stop_streaming(struct vb2_queue *vq) +{ + struct vsp1_video *video = vb2_get_drv_priv(vq); + struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); + unsigned long flags; + int ret; + + mutex_lock(&pipe->lock); + if (--pipe->stream_count == 0) { + /* Stop the pipeline. */ + ret = vsp1_pipeline_stop(pipe); + if (ret == -ETIMEDOUT) + dev_err(video->vsp1->dev, "pipeline stop timeout\n"); + } + mutex_unlock(&pipe->lock); + + vsp1_pipeline_cleanup(pipe); + media_entity_pipeline_stop(&video->video.entity); + + /* Remove all buffers from the IRQ queue. */ + spin_lock_irqsave(&video->irqlock, flags); + INIT_LIST_HEAD(&video->irqqueue); + spin_unlock_irqrestore(&video->irqlock, flags); + + return 0; +} + +static struct vb2_ops vsp1_video_queue_qops = { + .queue_setup = vsp1_video_queue_setup, + .buf_prepare = vsp1_video_buffer_prepare, + .buf_queue = vsp1_video_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = vsp1_video_start_streaming, + .stop_streaming = vsp1_video_stop_streaming, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int +vsp1_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) +{ + struct v4l2_fh *vfh = file->private_data; + struct vsp1_video *video = to_vsp1_video(vfh->vdev); + + cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING + | V4L2_CAP_VIDEO_CAPTURE_MPLANE + | V4L2_CAP_VIDEO_OUTPUT_MPLANE; + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE + | V4L2_CAP_STREAMING; + else + cap->device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE + | V4L2_CAP_STREAMING; + + strlcpy(cap->driver, "vsp1", sizeof(cap->driver)); + strlcpy(cap->card, video->video.name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(video->vsp1->dev)); + + return 0; +} + +static int +vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct vsp1_video *video = to_vsp1_video(vfh->vdev); + + if (format->type != video->queue.type) + return -EINVAL; + + mutex_lock(&video->lock); + format->fmt.pix_mp = video->format; + mutex_unlock(&video->lock); + + return 0; +} + +static int +vsp1_video_try_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct vsp1_video *video = to_vsp1_video(vfh->vdev); + + if (format->type != video->queue.type) + return -EINVAL; + + return __vsp1_video_try_format(video, &format->fmt.pix_mp, NULL); +} + +static int +vsp1_video_set_format(struct file *file, void *fh, struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct vsp1_video *video = to_vsp1_video(vfh->vdev); + const struct vsp1_format_info *info; + int ret; + + if (format->type != video->queue.type) + return -EINVAL; + + ret = __vsp1_video_try_format(video, &format->fmt.pix_mp, &info); + if (ret < 0) + return ret; + + mutex_lock(&video->lock); + + if (vb2_is_busy(&video->queue)) { + ret = -EBUSY; + goto done; + } + + video->format = format->fmt.pix_mp; + video->fmtinfo = info; + +done: + mutex_unlock(&video->lock); + return ret; +} + +static int +vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) +{ + struct v4l2_fh *vfh = file->private_data; + struct vsp1_video *video = to_vsp1_video(vfh->vdev); + struct vsp1_pipeline *pipe; + int ret; + + mutex_lock(&video->lock); + + if (video->queue.owner && video->queue.owner != file->private_data) + return -EBUSY; + + video->sequence = 0; + + /* Start streaming on the pipeline. No link touching an entity in the + * pipeline can be activated or deactivated once streaming is started. + * + * Use the VSP1 pipeline object embedded in the first video object that + * starts streaming. + */ + pipe = video->video.entity.pipe + ? to_vsp1_pipeline(&video->video.entity) : &video->pipe; + + ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe); + if (ret < 0) + return ret; + + /* Verify that the configured format matches the output of the connected + * subdev. + */ + ret = vsp1_video_verify_format(video); + if (ret < 0) + goto err_stop; + + ret = vsp1_pipeline_init(pipe, video); + if (ret < 0) + goto err_stop; + + /* Start the queue. */ + ret = vb2_streamon(&video->queue, type); + if (ret < 0) + goto err_cleanup; + + return 0; + +err_cleanup: + vsp1_pipeline_cleanup(pipe); +err_stop: + media_entity_pipeline_stop(&video->video.entity); + return ret; +} + +static const struct v4l2_ioctl_ops vsp1_video_ioctl_ops = { + .vidioc_querycap = vsp1_video_querycap, + .vidioc_g_fmt_vid_cap_mplane = vsp1_video_get_format, + .vidioc_s_fmt_vid_cap_mplane = vsp1_video_set_format, + .vidioc_try_fmt_vid_cap_mplane = vsp1_video_try_format, + .vidioc_g_fmt_vid_out_mplane = vsp1_video_get_format, + .vidioc_s_fmt_vid_out_mplane = vsp1_video_set_format, + .vidioc_try_fmt_vid_out_mplane = vsp1_video_try_format, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vsp1_video_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 File Operations + */ + +static int vsp1_video_open(struct file *file) +{ + struct vsp1_video *video = video_drvdata(file); + struct v4l2_fh *vfh; + int ret = 0; + + vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); + if (vfh == NULL) + return -ENOMEM; + + v4l2_fh_init(vfh, &video->video); + v4l2_fh_add(vfh); + + file->private_data = vfh; + + if (!vsp1_device_get(video->vsp1)) { + ret = -EBUSY; + v4l2_fh_del(vfh); + kfree(vfh); + } + + return ret; +} + +static int vsp1_video_release(struct file *file) +{ + struct vsp1_video *video = video_drvdata(file); + struct v4l2_fh *vfh = file->private_data; + + mutex_lock(&video->lock); + if (video->queue.owner == vfh) { + vb2_queue_release(&video->queue); + video->queue.owner = NULL; + } + mutex_unlock(&video->lock); + + vsp1_device_put(video->vsp1); + + v4l2_fh_release(file); + + file->private_data = NULL; + + return 0; +} + +static struct v4l2_file_operations vsp1_video_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = vsp1_video_open, + .release = vsp1_video_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf) +{ + const char *direction; + int ret; + + switch (video->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: + direction = "output"; + video->pad.flags = MEDIA_PAD_FL_SINK; + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: + direction = "input"; + video->pad.flags = MEDIA_PAD_FL_SOURCE; + video->video.vfl_dir = VFL_DIR_TX; + break; + + default: + return -EINVAL; + } + + video->rwpf = rwpf; + + mutex_init(&video->lock); + spin_lock_init(&video->irqlock); + INIT_LIST_HEAD(&video->irqqueue); + + mutex_init(&video->pipe.lock); + spin_lock_init(&video->pipe.irqlock); + INIT_LIST_HEAD(&video->pipe.entities); + init_waitqueue_head(&video->pipe.wq); + video->pipe.state = VSP1_PIPELINE_STOPPED; + + /* Initialize the media entity... */ + ret = media_entity_init(&video->video.entity, 1, &video->pad, 0); + if (ret < 0) + return ret; + + /* ... and the format ... */ + video->fmtinfo = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT); + video->format.pixelformat = video->fmtinfo->fourcc; + video->format.colorspace = V4L2_COLORSPACE_SRGB; + video->format.field = V4L2_FIELD_NONE; + video->format.width = VSP1_VIDEO_DEF_WIDTH; + video->format.height = VSP1_VIDEO_DEF_HEIGHT; + video->format.num_planes = 1; + video->format.plane_fmt[0].bytesperline = + video->format.width * video->fmtinfo->bpp[0] / 8; + video->format.plane_fmt[0].sizeimage = + video->format.plane_fmt[0].bytesperline * video->format.height; + + /* ... and the video node... */ + video->video.v4l2_dev = &video->vsp1->v4l2_dev; + video->video.fops = &vsp1_video_fops; + snprintf(video->video.name, sizeof(video->video.name), "%s %s", + rwpf->subdev.name, direction); + video->video.vfl_type = VFL_TYPE_GRABBER; + video->video.release = video_device_release_empty; + video->video.ioctl_ops = &vsp1_video_ioctl_ops; + + video_set_drvdata(&video->video, video); + + /* ... and the buffers queue... */ + video->alloc_ctx = vb2_dma_contig_init_ctx(video->vsp1->dev); + if (IS_ERR(video->alloc_ctx)) + goto error; + + video->queue.type = video->type; + video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + video->queue.lock = &video->lock; + video->queue.drv_priv = video; + video->queue.buf_struct_size = sizeof(struct vsp1_video_buffer); + video->queue.ops = &vsp1_video_queue_qops; + video->queue.mem_ops = &vb2_dma_contig_memops; + video->queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY; + ret = vb2_queue_init(&video->queue); + if (ret < 0) { + dev_err(video->vsp1->dev, "failed to initialize vb2 queue\n"); + goto error; + } + + /* ... and register the video device. */ + video->video.queue = &video->queue; + ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + dev_err(video->vsp1->dev, "failed to register video device\n"); + goto error; + } + + return 0; + +error: + vb2_dma_contig_cleanup_ctx(video->alloc_ctx); + vsp1_video_cleanup(video); + return ret; +} + +void vsp1_video_cleanup(struct vsp1_video *video) +{ + if (video_is_registered(&video->video)) + video_unregister_device(&video->video); + + vb2_dma_contig_cleanup_ctx(video->alloc_ctx); + media_entity_cleanup(&video->video.entity); +} diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h new file mode 100644 index 000000000000..d8612a378345 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -0,0 +1,144 @@ +/* + * vsp1_video.h -- R-Car VSP1 Video Node + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_VIDEO_H__ +#define __VSP1_VIDEO_H__ + +#include +#include +#include + +#include +#include + +struct vsp1_video; + +/* + * struct vsp1_format_info - VSP1 video format description + * @mbus: media bus format code + * @fourcc: V4L2 pixel format FCC identifier + * @planes: number of planes + * @bpp: bits per pixel + * @hwfmt: VSP1 hardware format + * @swap_yc: the Y and C components are swapped (Y comes before C) + * @swap_uv: the U and V components are swapped (V comes before U) + * @hsub: horizontal subsampling factor + * @vsub: vertical subsampling factor + */ +struct vsp1_format_info { + u32 fourcc; + unsigned int mbus; + unsigned int hwfmt; + unsigned int swap; + unsigned int planes; + unsigned int bpp[3]; + bool swap_yc; + bool swap_uv; + unsigned int hsub; + unsigned int vsub; +}; + +enum vsp1_pipeline_state { + VSP1_PIPELINE_STOPPED, + VSP1_PIPELINE_RUNNING, + VSP1_PIPELINE_STOPPING, +}; + +/* + * struct vsp1_pipeline - A VSP1 hardware pipeline + * @media: the media pipeline + * @irqlock: protects the pipeline state + * @lock: protects the pipeline use count and stream count + */ +struct vsp1_pipeline { + struct media_pipeline pipe; + + spinlock_t irqlock; + enum vsp1_pipeline_state state; + wait_queue_head_t wq; + + struct mutex lock; + unsigned int use_count; + unsigned int stream_count; + unsigned int buffers_ready; + + unsigned int num_video; + unsigned int num_inputs; + struct vsp1_rwpf *inputs[VPS1_MAX_RPF]; + struct vsp1_rwpf *output; + struct vsp1_entity *lif; + + struct list_head entities; +}; + +static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e) +{ + if (likely(e->pipe)) + return container_of(e->pipe, struct vsp1_pipeline, pipe); + else + return NULL; +} + +struct vsp1_video_buffer { + struct vsp1_video *video; + struct vb2_buffer buf; + struct list_head queue; + + dma_addr_t addr[3]; + unsigned int length[3]; +}; + +static inline struct vsp1_video_buffer * +to_vsp1_video_buffer(struct vb2_buffer *vb) +{ + return container_of(vb, struct vsp1_video_buffer, buf); +} + +struct vsp1_video_operations { + void (*queue)(struct vsp1_video *video, struct vsp1_video_buffer *buf); +}; + +struct vsp1_video { + struct vsp1_device *vsp1; + struct vsp1_entity *rwpf; + + const struct vsp1_video_operations *ops; + + struct video_device video; + enum v4l2_buf_type type; + struct media_pad pad; + + struct mutex lock; + struct v4l2_pix_format_mplane format; + const struct vsp1_format_info *fmtinfo; + + struct vsp1_pipeline pipe; + unsigned int pipe_index; + + struct vb2_queue queue; + void *alloc_ctx; + spinlock_t irqlock; + struct list_head irqqueue; + unsigned int sequence; +}; + +static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev) +{ + return container_of(vdev, struct vsp1_video, video); +} + +int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf); +void vsp1_video_cleanup(struct vsp1_video *video); + +void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe); + +#endif /* __VSP1_VIDEO_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c new file mode 100644 index 000000000000..db4b85ee05fc --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -0,0 +1,233 @@ +/* + * vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include + +#include + +#include "vsp1.h" +#include "vsp1_rwpf.h" +#include "vsp1_video.h" + +#define WPF_MAX_WIDTH 2048 +#define WPF_MAX_HEIGHT 2048 + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_wpf_read(struct vsp1_rwpf *wpf, u32 reg) +{ + return vsp1_read(wpf->entity.vsp1, + reg + wpf->entity.index * VI6_WPF_OFFSET); +} + +static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data) +{ + vsp1_write(wpf->entity.vsp1, + reg + wpf->entity.index * VI6_WPF_OFFSET, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Core Operations + */ + +static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct vsp1_rwpf *wpf = to_rwpf(subdev); + struct vsp1_pipeline *pipe = + to_vsp1_pipeline(&wpf->entity.subdev.entity); + struct vsp1_device *vsp1 = wpf->entity.vsp1; + const struct v4l2_mbus_framefmt *format = + &wpf->entity.formats[RWPF_PAD_SOURCE]; + unsigned int i; + u32 srcrpf = 0; + u32 outfmt = 0; + + if (!enable) { + vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0); + return 0; + } + + /* Sources */ + for (i = 0; i < pipe->num_inputs; ++i) { + struct vsp1_rwpf *input = pipe->inputs[i]; + + srcrpf |= VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index); + } + + vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf); + + /* Destination stride. Cropping isn't supported yet. */ + if (!pipe->lif) { + struct v4l2_pix_format_mplane *format = &wpf->video.format; + + vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_Y, + format->plane_fmt[0].bytesperline); + if (format->num_planes > 1) + vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_C, + format->plane_fmt[1].bytesperline); + } + + vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP, + format->width << VI6_WPF_SZCLIP_SIZE_SHIFT); + vsp1_wpf_write(wpf, VI6_WPF_VSZCLIP, + format->height << VI6_WPF_SZCLIP_SIZE_SHIFT); + + /* Format */ + if (!pipe->lif) { + const struct vsp1_format_info *fmtinfo = wpf->video.fmtinfo; + + outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT; + + if (fmtinfo->swap_yc) + outfmt |= VI6_WPF_OUTFMT_SPYCS; + if (fmtinfo->swap_uv) + outfmt |= VI6_WPF_OUTFMT_SPUVS; + + vsp1_wpf_write(wpf, VI6_WPF_DSWAP, fmtinfo->swap); + } + + if (wpf->entity.formats[RWPF_PAD_SINK].code != + wpf->entity.formats[RWPF_PAD_SOURCE].code) + outfmt |= VI6_WPF_OUTFMT_CSC; + + vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt); + + vsp1_write(vsp1, VI6_DPR_WPF_FPORCH(wpf->entity.index), + VI6_DPR_WPF_FPORCH_FP_WPFN); + + vsp1_write(vsp1, VI6_WPF_WRBCK_CTRL, 0); + + /* Enable interrupts */ + vsp1_write(vsp1, VI6_WPF_IRQ_STA(wpf->entity.index), 0); + vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), + VI6_WFP_IRQ_ENB_FREE); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static struct v4l2_subdev_video_ops wpf_video_ops = { + .s_stream = wpf_s_stream, +}; + +static struct v4l2_subdev_pad_ops wpf_pad_ops = { + .enum_mbus_code = vsp1_rwpf_enum_mbus_code, + .enum_frame_size = vsp1_rwpf_enum_frame_size, + .get_fmt = vsp1_rwpf_get_format, + .set_fmt = vsp1_rwpf_set_format, +}; + +static struct v4l2_subdev_ops wpf_ops = { + .video = &wpf_video_ops, + .pad = &wpf_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * Video Device Operations + */ + +static void wpf_vdev_queue(struct vsp1_video *video, + struct vsp1_video_buffer *buf) +{ + struct vsp1_rwpf *wpf = container_of(video, struct vsp1_rwpf, video); + + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, buf->addr[0]); + if (buf->buf.num_planes > 1) + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, buf->addr[1]); + if (buf->buf.num_planes > 2) + vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, buf->addr[2]); +} + +static const struct vsp1_video_operations wpf_vdev_ops = { + .queue = wpf_vdev_queue, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) +{ + struct v4l2_subdev *subdev; + struct vsp1_video *video; + struct vsp1_rwpf *wpf; + unsigned int flags; + int ret; + + wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL); + if (wpf == NULL) + return ERR_PTR(-ENOMEM); + + wpf->max_width = WPF_MAX_WIDTH; + wpf->max_height = WPF_MAX_HEIGHT; + + wpf->entity.type = VSP1_ENTITY_WPF; + wpf->entity.index = index; + wpf->entity.id = VI6_DPR_NODE_WPF(index); + + ret = vsp1_entity_init(vsp1, &wpf->entity, 2); + if (ret < 0) + return ERR_PTR(ret); + + /* Initialize the V4L2 subdev. */ + subdev = &wpf->entity.subdev; + v4l2_subdev_init(subdev, &wpf_ops); + + subdev->entity.ops = &vsp1_media_ops; + subdev->internal_ops = &vsp1_subdev_internal_ops; + snprintf(subdev->name, sizeof(subdev->name), "%s wpf.%u", + dev_name(vsp1->dev), index); + v4l2_set_subdevdata(subdev, wpf); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + vsp1_entity_init_formats(subdev, NULL); + + /* Initialize the video device. */ + video = &wpf->video; + + video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + video->vsp1 = vsp1; + video->ops = &wpf_vdev_ops; + + ret = vsp1_video_init(video, &wpf->entity); + if (ret < 0) + goto error_video; + + /* Connect the video device to the WPF. All connections are immutable + * except for the WPF0 source link if a LIF is present. + */ + flags = MEDIA_LNK_FL_ENABLED; + if (!(vsp1->pdata->features & VSP1_HAS_LIF) || index != 0) + flags |= MEDIA_LNK_FL_IMMUTABLE; + + ret = media_entity_create_link(&wpf->entity.subdev.entity, + RWPF_PAD_SOURCE, + &wpf->video.video.entity, 0, flags); + if (ret < 0) + goto error_link; + + wpf->entity.sink = &wpf->video.video.entity; + + return wpf; + +error_link: + vsp1_video_cleanup(video); +error_video: + media_entity_cleanup(&wpf->entity.subdev.entity); + return ERR_PTR(ret); +} diff --git a/include/linux/platform_data/vsp1.h b/include/linux/platform_data/vsp1.h new file mode 100644 index 000000000000..a73a456d7f11 --- /dev/null +++ b/include/linux/platform_data/vsp1.h @@ -0,0 +1,25 @@ +/* + * vsp1.h -- R-Car VSP1 Platform Data + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __PLATFORM_VSP1_H__ +#define __PLATFORM_VSP1_H__ + +#define VSP1_HAS_LIF (1 << 0) + +struct vsp1_platform_data { + unsigned int features; + unsigned int rpf_count; + unsigned int uds_count; + unsigned int wpf_count; +}; + +#endif /* __PLATFORM_VSP1_H__ */ -- cgit v1.2.3-71-gd317 From 59b564599bc66f086856b09e480f89555d47b35c Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Sun, 28 Jul 2013 16:01:43 -0300 Subject: [media] tea575x: Move header from sound to media Move include/sound/tea575x-tuner.h to include/media/tea575x.h and update files that include it. Signed-off-by: Ondrej Zary Acked-by: Takashi Iwai Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/radio-maxiradio.c | 2 +- drivers/media/radio/radio-sf16fmr2.c | 2 +- drivers/media/radio/radio-shark.c | 2 +- include/media/tea575x.h | 79 +++++++++++++++++++++++++++++++++++ include/sound/tea575x-tuner.h | 79 ----------------------------------- sound/i2c/other/tea575x-tuner.c | 2 +- sound/pci/es1968.c | 2 +- sound/pci/fm801.c | 2 +- 8 files changed, 85 insertions(+), 85 deletions(-) create mode 100644 include/media/tea575x.h delete mode 100644 include/sound/tea575x-tuner.h (limited to 'include') diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c index 1d1c9e1d386e..5236035f0f2a 100644 --- a/drivers/media/radio/radio-maxiradio.c +++ b/drivers/media/radio/radio-maxiradio.c @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index 9c0990457a7c..f1e3714b5f16 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -14,7 +14,7 @@ #include /* outb, outb_p */ #include #include -#include +#include MODULE_AUTHOR("Ondrej Zary"); MODULE_DESCRIPTION("MediaForte SF16-FMR2 and SF16-FMD2 FM radio card driver"); diff --git a/drivers/media/radio/radio-shark.c b/drivers/media/radio/radio-shark.c index 8fa18ab5b725..b91477212413 100644 --- a/drivers/media/radio/radio-shark.c +++ b/drivers/media/radio/radio-shark.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #if defined(CONFIG_LEDS_CLASS) || \ (defined(CONFIG_LEDS_CLASS_MODULE) && defined(CONFIG_RADIO_SHARK_MODULE)) diff --git a/include/media/tea575x.h b/include/media/tea575x.h new file mode 100644 index 000000000000..2d4fa59db902 --- /dev/null +++ b/include/media/tea575x.h @@ -0,0 +1,79 @@ +#ifndef __SOUND_TEA575X_TUNER_H +#define __SOUND_TEA575X_TUNER_H + +/* + * ALSA driver for TEA5757/5759 Philips AM/FM tuner chips + * + * Copyright (c) 2004 Jaroslav Kysela + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +#define TEA575X_FMIF 10700 +#define TEA575X_AMIF 450 + +#define TEA575X_DATA (1 << 0) +#define TEA575X_CLK (1 << 1) +#define TEA575X_WREN (1 << 2) +#define TEA575X_MOST (1 << 3) + +struct snd_tea575x; + +struct snd_tea575x_ops { + /* Drivers using snd_tea575x must either define read_ and write_val */ + void (*write_val)(struct snd_tea575x *tea, u32 val); + u32 (*read_val)(struct snd_tea575x *tea); + /* Or define the 3 pin functions */ + void (*set_pins)(struct snd_tea575x *tea, u8 pins); + u8 (*get_pins)(struct snd_tea575x *tea); + void (*set_direction)(struct snd_tea575x *tea, bool output); +}; + +struct snd_tea575x { + struct v4l2_device *v4l2_dev; + struct v4l2_file_operations fops; + struct video_device vd; /* video device */ + int radio_nr; /* radio_nr */ + bool tea5759; /* 5759 chip is present */ + bool has_am; /* Device can tune to AM freqs */ + bool cannot_read_data; /* Device cannot read the data pin */ + bool cannot_mute; /* Device cannot mute */ + bool mute; /* Device is muted? */ + bool stereo; /* receiving stereo */ + bool tuned; /* tuned to a station */ + unsigned int val; /* hw value */ + u32 band; /* 0: FM, 1: FM-Japan, 2: AM */ + u32 freq; /* frequency */ + struct mutex mutex; + struct snd_tea575x_ops *ops; + void *private_data; + u8 card[32]; + u8 bus_info[32]; + struct v4l2_ctrl_handler ctrl_handler; + int (*ext_init)(struct snd_tea575x *tea); +}; + +int snd_tea575x_hw_init(struct snd_tea575x *tea); +int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner); +void snd_tea575x_exit(struct snd_tea575x *tea); +void snd_tea575x_set_freq(struct snd_tea575x *tea); + +#endif /* __SOUND_TEA575X_TUNER_H */ diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h deleted file mode 100644 index 2d4fa59db902..000000000000 --- a/include/sound/tea575x-tuner.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef __SOUND_TEA575X_TUNER_H -#define __SOUND_TEA575X_TUNER_H - -/* - * ALSA driver for TEA5757/5759 Philips AM/FM tuner chips - * - * Copyright (c) 2004 Jaroslav Kysela - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include - -#define TEA575X_FMIF 10700 -#define TEA575X_AMIF 450 - -#define TEA575X_DATA (1 << 0) -#define TEA575X_CLK (1 << 1) -#define TEA575X_WREN (1 << 2) -#define TEA575X_MOST (1 << 3) - -struct snd_tea575x; - -struct snd_tea575x_ops { - /* Drivers using snd_tea575x must either define read_ and write_val */ - void (*write_val)(struct snd_tea575x *tea, u32 val); - u32 (*read_val)(struct snd_tea575x *tea); - /* Or define the 3 pin functions */ - void (*set_pins)(struct snd_tea575x *tea, u8 pins); - u8 (*get_pins)(struct snd_tea575x *tea); - void (*set_direction)(struct snd_tea575x *tea, bool output); -}; - -struct snd_tea575x { - struct v4l2_device *v4l2_dev; - struct v4l2_file_operations fops; - struct video_device vd; /* video device */ - int radio_nr; /* radio_nr */ - bool tea5759; /* 5759 chip is present */ - bool has_am; /* Device can tune to AM freqs */ - bool cannot_read_data; /* Device cannot read the data pin */ - bool cannot_mute; /* Device cannot mute */ - bool mute; /* Device is muted? */ - bool stereo; /* receiving stereo */ - bool tuned; /* tuned to a station */ - unsigned int val; /* hw value */ - u32 band; /* 0: FM, 1: FM-Japan, 2: AM */ - u32 freq; /* frequency */ - struct mutex mutex; - struct snd_tea575x_ops *ops; - void *private_data; - u8 card[32]; - u8 bus_info[32]; - struct v4l2_ctrl_handler ctrl_handler; - int (*ext_init)(struct snd_tea575x *tea); -}; - -int snd_tea575x_hw_init(struct snd_tea575x *tea); -int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner); -void snd_tea575x_exit(struct snd_tea575x *tea); -void snd_tea575x_set_freq(struct snd_tea575x *tea); - -#endif /* __SOUND_TEA575X_TUNER_H */ diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index 46ec4dff094b..cef06981b7c9 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips"); diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 5e2ec9687731..b0e3d92c4656 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -113,7 +113,7 @@ #include #ifdef CONFIG_SND_ES1968_RADIO -#include +#include #endif #define CARD_NAME "ESS Maestro1/2" diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index 706c5b67b708..45bc8a95b7c4 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -37,7 +37,7 @@ #include #ifdef CONFIG_SND_FM801_TEA575X_BOOL -#include +#include #endif MODULE_AUTHOR("Jaroslav Kysela "); -- cgit v1.2.3-71-gd317 From 299878fa3c373dbf74edf5872c79ef4c65b80a04 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:40:54 -0300 Subject: [media] v4l2-dv-timings.h: remove duplicate V4L2_DV_BT_DMT_1366X768P60 This particular DMT timing definition was duplicated in the header. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/v4l2-dv-timings.h | 8 -------- 1 file changed, 8 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/v4l2-dv-timings.h b/include/uapi/linux/v4l2-dv-timings.h index 4e0c58d25ff0..be709fe29552 100644 --- a/include/uapi/linux/v4l2-dv-timings.h +++ b/include/uapi/linux/v4l2-dv-timings.h @@ -823,12 +823,4 @@ V4L2_DV_FL_REDUCED_BLANKING) \ } -#define V4L2_DV_BT_DMT_1366X768P60 { \ - .type = V4L2_DV_BT_656_1120, \ - V4L2_INIT_BT_TIMINGS(1366, 768, 0, \ - V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ - 85500000, 70, 143, 213, 3, 3, 24, 0, 0, 0, \ - V4L2_DV_BT_STD_DMT, 0) \ -} - #endif -- cgit v1.2.3-71-gd317 From b18787ed1ce32eb0c2ce2323220abd4ed93c4b97 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:40:55 -0300 Subject: [media] v4l2-dv-timings: add new helper module This module makes it easy to filter valid timings from the full list of CEA and DMT timings based on the timings capabilities. Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/Makefile | 1 + drivers/media/v4l2-core/v4l2-dv-timings.c | 192 ++++++++++++++++++++++++++++++ include/media/v4l2-dv-timings.h | 67 +++++++++++ 3 files changed, 260 insertions(+) create mode 100644 drivers/media/v4l2-core/v4l2-dv-timings.c create mode 100644 include/media/v4l2-dv-timings.h (limited to 'include') diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile index 4c33b8d6520c..1a85eee581f8 100644 --- a/drivers/media/v4l2-core/Makefile +++ b/drivers/media/v4l2-core/Makefile @@ -17,6 +17,7 @@ endif obj-$(CONFIG_VIDEO_V4L2) += videodev.o obj-$(CONFIG_VIDEO_V4L2_INT_DEVICE) += v4l2-int-device.o obj-$(CONFIG_VIDEO_V4L2) += v4l2-common.o +obj-$(CONFIG_VIDEO_V4L2) += v4l2-dv-timings.o obj-$(CONFIG_VIDEO_TUNER) += tuner.o diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c new file mode 100644 index 000000000000..58279467a7a5 --- /dev/null +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -0,0 +1,192 @@ +/* + * v4l2-dv-timings - dv-timings helper functions + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct v4l2_dv_timings timings[] = { + V4L2_DV_BT_CEA_640X480P59_94, + V4L2_DV_BT_CEA_720X480I59_94, + V4L2_DV_BT_CEA_720X480P59_94, + V4L2_DV_BT_CEA_720X576I50, + V4L2_DV_BT_CEA_720X576P50, + V4L2_DV_BT_CEA_1280X720P24, + V4L2_DV_BT_CEA_1280X720P25, + V4L2_DV_BT_CEA_1280X720P30, + V4L2_DV_BT_CEA_1280X720P50, + V4L2_DV_BT_CEA_1280X720P60, + V4L2_DV_BT_CEA_1920X1080P24, + V4L2_DV_BT_CEA_1920X1080P25, + V4L2_DV_BT_CEA_1920X1080P30, + V4L2_DV_BT_CEA_1920X1080I50, + V4L2_DV_BT_CEA_1920X1080P50, + V4L2_DV_BT_CEA_1920X1080I60, + V4L2_DV_BT_CEA_1920X1080P60, + V4L2_DV_BT_DMT_640X350P85, + V4L2_DV_BT_DMT_640X400P85, + V4L2_DV_BT_DMT_720X400P85, + V4L2_DV_BT_DMT_640X480P72, + V4L2_DV_BT_DMT_640X480P75, + V4L2_DV_BT_DMT_640X480P85, + V4L2_DV_BT_DMT_800X600P56, + V4L2_DV_BT_DMT_800X600P60, + V4L2_DV_BT_DMT_800X600P72, + V4L2_DV_BT_DMT_800X600P75, + V4L2_DV_BT_DMT_800X600P85, + V4L2_DV_BT_DMT_800X600P120_RB, + V4L2_DV_BT_DMT_848X480P60, + V4L2_DV_BT_DMT_1024X768I43, + V4L2_DV_BT_DMT_1024X768P60, + V4L2_DV_BT_DMT_1024X768P70, + V4L2_DV_BT_DMT_1024X768P75, + V4L2_DV_BT_DMT_1024X768P85, + V4L2_DV_BT_DMT_1024X768P120_RB, + V4L2_DV_BT_DMT_1152X864P75, + V4L2_DV_BT_DMT_1280X768P60_RB, + V4L2_DV_BT_DMT_1280X768P60, + V4L2_DV_BT_DMT_1280X768P75, + V4L2_DV_BT_DMT_1280X768P85, + V4L2_DV_BT_DMT_1280X768P120_RB, + V4L2_DV_BT_DMT_1280X800P60_RB, + V4L2_DV_BT_DMT_1280X800P60, + V4L2_DV_BT_DMT_1280X800P75, + V4L2_DV_BT_DMT_1280X800P85, + V4L2_DV_BT_DMT_1280X800P120_RB, + V4L2_DV_BT_DMT_1280X960P60, + V4L2_DV_BT_DMT_1280X960P85, + V4L2_DV_BT_DMT_1280X960P120_RB, + V4L2_DV_BT_DMT_1280X1024P60, + V4L2_DV_BT_DMT_1280X1024P75, + V4L2_DV_BT_DMT_1280X1024P85, + V4L2_DV_BT_DMT_1280X1024P120_RB, + V4L2_DV_BT_DMT_1360X768P60, + V4L2_DV_BT_DMT_1360X768P120_RB, + V4L2_DV_BT_DMT_1366X768P60, + V4L2_DV_BT_DMT_1366X768P60_RB, + V4L2_DV_BT_DMT_1400X1050P60_RB, + V4L2_DV_BT_DMT_1400X1050P60, + V4L2_DV_BT_DMT_1400X1050P75, + V4L2_DV_BT_DMT_1400X1050P85, + V4L2_DV_BT_DMT_1400X1050P120_RB, + V4L2_DV_BT_DMT_1440X900P60_RB, + V4L2_DV_BT_DMT_1440X900P60, + V4L2_DV_BT_DMT_1440X900P75, + V4L2_DV_BT_DMT_1440X900P85, + V4L2_DV_BT_DMT_1440X900P120_RB, + V4L2_DV_BT_DMT_1600X900P60_RB, + V4L2_DV_BT_DMT_1600X1200P60, + V4L2_DV_BT_DMT_1600X1200P65, + V4L2_DV_BT_DMT_1600X1200P70, + V4L2_DV_BT_DMT_1600X1200P75, + V4L2_DV_BT_DMT_1600X1200P85, + V4L2_DV_BT_DMT_1600X1200P120_RB, + V4L2_DV_BT_DMT_1680X1050P60_RB, + V4L2_DV_BT_DMT_1680X1050P60, + V4L2_DV_BT_DMT_1680X1050P75, + V4L2_DV_BT_DMT_1680X1050P85, + V4L2_DV_BT_DMT_1680X1050P120_RB, + V4L2_DV_BT_DMT_1792X1344P60, + V4L2_DV_BT_DMT_1792X1344P75, + V4L2_DV_BT_DMT_1792X1344P120_RB, + V4L2_DV_BT_DMT_1856X1392P60, + V4L2_DV_BT_DMT_1856X1392P75, + V4L2_DV_BT_DMT_1856X1392P120_RB, + V4L2_DV_BT_DMT_1920X1200P60_RB, + V4L2_DV_BT_DMT_1920X1200P60, + V4L2_DV_BT_DMT_1920X1200P75, + V4L2_DV_BT_DMT_1920X1200P85, + V4L2_DV_BT_DMT_1920X1200P120_RB, + V4L2_DV_BT_DMT_1920X1440P60, + V4L2_DV_BT_DMT_1920X1440P75, + V4L2_DV_BT_DMT_1920X1440P120_RB, + V4L2_DV_BT_DMT_2048X1152P60_RB, + V4L2_DV_BT_DMT_2560X1600P60_RB, + V4L2_DV_BT_DMT_2560X1600P60, + V4L2_DV_BT_DMT_2560X1600P75, + V4L2_DV_BT_DMT_2560X1600P85, + V4L2_DV_BT_DMT_2560X1600P120_RB, +}; + +bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, + const struct v4l2_dv_timings_cap *dvcap) +{ + const struct v4l2_bt_timings *bt = &t->bt; + const struct v4l2_bt_timings_cap *cap = &dvcap->bt; + u32 caps = cap->capabilities; + + if (t->type != V4L2_DV_BT_656_1120) + return false; + if (t->type != dvcap->type || + bt->height < cap->min_height || + bt->height > cap->max_height || + bt->width < cap->min_width || + bt->width > cap->max_width || + bt->pixelclock < cap->min_pixelclock || + bt->pixelclock > cap->max_pixelclock || + (cap->standards && !(bt->standards & cap->standards)) || + (bt->interlaced && !(caps & V4L2_DV_BT_CAP_INTERLACED)) || + (!bt->interlaced && !(caps & V4L2_DV_BT_CAP_PROGRESSIVE))) + return false; + return true; +} +EXPORT_SYMBOL_GPL(v4l2_dv_valid_timings); + +int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, + const struct v4l2_dv_timings_cap *cap) +{ + u32 i, idx; + + memset(t->reserved, 0, sizeof(t->reserved)); + for (i = idx = 0; i < ARRAY_SIZE(timings); i++) { + if (v4l2_dv_valid_timings(timings + i, cap) && + idx++ == t->index) { + t->timings = timings[i]; + return 0; + } + } + return -EINVAL; +} +EXPORT_SYMBOL_GPL(v4l2_enum_dv_timings_cap); + +bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, + const struct v4l2_dv_timings_cap *cap, + unsigned pclock_delta) +{ + int i; + + if (!v4l2_dv_valid_timings(t, cap)) + return false; + + for (i = 0; i < ARRAY_SIZE(timings); i++) { + if (v4l2_dv_valid_timings(timings + i, cap) && + v4l_match_dv_timings(t, timings + i, pclock_delta)) { + *t = timings[i]; + return true; + } + } + return false; +} +EXPORT_SYMBOL_GPL(v4l2_find_dv_timings_cap); diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h new file mode 100644 index 000000000000..41075fa02a96 --- /dev/null +++ b/include/media/v4l2-dv-timings.h @@ -0,0 +1,67 @@ +/* + * v4l2-dv-timings - Internal header with dv-timings helper functions + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef __V4L2_DV_TIMINGS_H +#define __V4L2_DV_TIMINGS_H + +#include + +/** v4l2_dv_valid_timings() - are these timings valid? + * @t: the v4l2_dv_timings struct. + * @cap: the v4l2_dv_timings_cap capabilities. + * + * Returns true if the given dv_timings struct is supported by the + * hardware capabilities, returns false otherwise. + */ +bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, + const struct v4l2_dv_timings_cap *cap); + +/** v4l2_enum_dv_timings_cap() - Helper function to enumerate possible DV timings based on capabilities + * @t: the v4l2_enum_dv_timings struct. + * @cap: the v4l2_dv_timings_cap capabilities. + * + * This enumerates dv_timings using the full list of possible CEA-861 and DMT + * timings, filtering out any timings that are not supported based on the + * hardware capabilities. + * + * If a valid timing for the given index is found, it will fill in @t and + * return 0, otherwise it returns -EINVAL. + */ +int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, + const struct v4l2_dv_timings_cap *cap); + +/** v4l2_find_dv_timings_cap() - Find the closest timings struct + * @t: the v4l2_enum_dv_timings struct. + * @cap: the v4l2_dv_timings_cap capabilities. + * @pclock_delta: maximum delta between t->pixelclock and the timing struct + * under consideration. + * + * This function tries to map the given timings to an entry in the + * full list of possible CEA-861 and DMT timings, filtering out any timings + * that are not supported based on the hardware capabilities. + * + * On success it will fill in @t with the found timings and it returns true. + * On failure it will return false. + */ +bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, + const struct v4l2_dv_timings_cap *cap, + unsigned pclock_delta); + +#endif -- cgit v1.2.3-71-gd317 From 2576415846bcbad3c0a6885fc44f950837106364 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:40:56 -0300 Subject: [media] v4l2: move dv-timings related code to v4l2-dv-timings.c v4l2-common.c contained a bunch of dv-timings related functions. Move that to the new v4l2-dv-timings.c which is a more appropriate place for them. There aren't many drivers that do HDTV, so it is a good idea to separate common code related to that into a module of its own. Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ad9389b.c | 1 + drivers/media/i2c/adv7604.c | 1 + drivers/media/i2c/ths8200.c | 1 + drivers/media/usb/hdpvr/hdpvr-video.c | 1 + drivers/media/v4l2-core/v4l2-common.c | 357 ----------------------------- drivers/media/v4l2-core/v4l2-dv-timings.c | 358 +++++++++++++++++++++++++++++- include/media/v4l2-common.h | 13 -- include/media/v4l2-dv-timings.h | 59 +++++ 8 files changed, 420 insertions(+), 371 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index ba4364dfae66..2fa8d7286cea 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 1d675b58fd71..181a6c359335 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -38,6 +38,7 @@ #include #include #include +#include #include static int debug; diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 8a29810d155a..aef7c0e7cd67 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -21,6 +21,7 @@ #include #include +#include #include #include diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index 4f8567aa99d8..9c67b6e127e3 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include "hdpvr.h" diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index a95e5e23403f..037d7a55aa8c 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -495,363 +495,6 @@ void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, } EXPORT_SYMBOL_GPL(v4l_bound_align_image); -/** - * v4l_match_dv_timings - check if two timings match - * @t1 - compare this v4l2_dv_timings struct... - * @t2 - with this struct. - * @pclock_delta - the allowed pixelclock deviation. - * - * Compare t1 with t2 with a given margin of error for the pixelclock. - */ -bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, - const struct v4l2_dv_timings *t2, - unsigned pclock_delta) -{ - if (t1->type != t2->type || t1->type != V4L2_DV_BT_656_1120) - return false; - if (t1->bt.width == t2->bt.width && - t1->bt.height == t2->bt.height && - t1->bt.interlaced == t2->bt.interlaced && - t1->bt.polarities == t2->bt.polarities && - t1->bt.pixelclock >= t2->bt.pixelclock - pclock_delta && - t1->bt.pixelclock <= t2->bt.pixelclock + pclock_delta && - t1->bt.hfrontporch == t2->bt.hfrontporch && - t1->bt.vfrontporch == t2->bt.vfrontporch && - t1->bt.vsync == t2->bt.vsync && - t1->bt.vbackporch == t2->bt.vbackporch && - (!t1->bt.interlaced || - (t1->bt.il_vfrontporch == t2->bt.il_vfrontporch && - t1->bt.il_vsync == t2->bt.il_vsync && - t1->bt.il_vbackporch == t2->bt.il_vbackporch))) - return true; - return false; -} -EXPORT_SYMBOL_GPL(v4l_match_dv_timings); - -/* - * CVT defines - * Based on Coordinated Video Timings Standard - * version 1.1 September 10, 2003 - */ - -#define CVT_PXL_CLK_GRAN 250000 /* pixel clock granularity */ - -/* Normal blanking */ -#define CVT_MIN_V_BPORCH 7 /* lines */ -#define CVT_MIN_V_PORCH_RND 3 /* lines */ -#define CVT_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */ - -/* Normal blanking for CVT uses GTF to calculate horizontal blanking */ -#define CVT_CELL_GRAN 8 /* character cell granularity */ -#define CVT_M 600 /* blanking formula gradient */ -#define CVT_C 40 /* blanking formula offset */ -#define CVT_K 128 /* blanking formula scaling factor */ -#define CVT_J 20 /* blanking formula scaling factor */ -#define CVT_C_PRIME (((CVT_C - CVT_J) * CVT_K / 256) + CVT_J) -#define CVT_M_PRIME (CVT_K * CVT_M / 256) - -/* Reduced Blanking */ -#define CVT_RB_MIN_V_BPORCH 7 /* lines */ -#define CVT_RB_V_FPORCH 3 /* lines */ -#define CVT_RB_MIN_V_BLANK 460 /* us */ -#define CVT_RB_H_SYNC 32 /* pixels */ -#define CVT_RB_H_BPORCH 80 /* pixels */ -#define CVT_RB_H_BLANK 160 /* pixels */ - -/** v4l2_detect_cvt - detect if the given timings follow the CVT standard - * @frame_height - the total height of the frame (including blanking) in lines. - * @hfreq - the horizontal frequency in Hz. - * @vsync - the height of the vertical sync in lines. - * @polarities - the horizontal and vertical polarities (same as struct - * v4l2_bt_timings polarities). - * @fmt - the resulting timings. - * - * This function will attempt to detect if the given values correspond to a - * valid CVT format. If so, then it will return true, and fmt will be filled - * in with the found CVT timings. - */ -bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, - u32 polarities, struct v4l2_dv_timings *fmt) -{ - int v_fp, v_bp, h_fp, h_bp, hsync; - int frame_width, image_height, image_width; - bool reduced_blanking; - unsigned pix_clk; - - if (vsync < 4 || vsync > 7) - return false; - - if (polarities == V4L2_DV_VSYNC_POS_POL) - reduced_blanking = false; - else if (polarities == V4L2_DV_HSYNC_POS_POL) - reduced_blanking = true; - else - return false; - - /* Vertical */ - if (reduced_blanking) { - v_fp = CVT_RB_V_FPORCH; - v_bp = (CVT_RB_MIN_V_BLANK * hfreq + 999999) / 1000000; - v_bp -= vsync + v_fp; - - if (v_bp < CVT_RB_MIN_V_BPORCH) - v_bp = CVT_RB_MIN_V_BPORCH; - } else { - v_fp = CVT_MIN_V_PORCH_RND; - v_bp = (CVT_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; - - if (v_bp < CVT_MIN_V_BPORCH) - v_bp = CVT_MIN_V_BPORCH; - } - image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; - - /* Aspect ratio based on vsync */ - switch (vsync) { - case 4: - image_width = (image_height * 4) / 3; - break; - case 5: - image_width = (image_height * 16) / 9; - break; - case 6: - image_width = (image_height * 16) / 10; - break; - case 7: - /* special case */ - if (image_height == 1024) - image_width = (image_height * 5) / 4; - else if (image_height == 768) - image_width = (image_height * 15) / 9; - else - return false; - break; - default: - return false; - } - - image_width = image_width & ~7; - - /* Horizontal */ - if (reduced_blanking) { - pix_clk = (image_width + CVT_RB_H_BLANK) * hfreq; - pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; - - h_bp = CVT_RB_H_BPORCH; - hsync = CVT_RB_H_SYNC; - h_fp = CVT_RB_H_BLANK - h_bp - hsync; - - frame_width = image_width + CVT_RB_H_BLANK; - } else { - int h_blank; - unsigned ideal_duty_cycle = CVT_C_PRIME - (CVT_M_PRIME * 1000) / hfreq; - - h_blank = (image_width * ideal_duty_cycle + (100 - ideal_duty_cycle) / 2) / - (100 - ideal_duty_cycle); - h_blank = h_blank - h_blank % (2 * CVT_CELL_GRAN); - - if (h_blank * 100 / image_width < 20) { - h_blank = image_width / 5; - h_blank = (h_blank + 0x7) & ~0x7; - } - - pix_clk = (image_width + h_blank) * hfreq; - pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; - - h_bp = h_blank / 2; - frame_width = image_width + h_blank; - - hsync = (frame_width * 8 + 50) / 100; - hsync = hsync - hsync % CVT_CELL_GRAN; - h_fp = h_blank - hsync - h_bp; - } - - fmt->bt.polarities = polarities; - fmt->bt.width = image_width; - fmt->bt.height = image_height; - fmt->bt.hfrontporch = h_fp; - fmt->bt.vfrontporch = v_fp; - fmt->bt.hsync = hsync; - fmt->bt.vsync = vsync; - fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; - fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; - fmt->bt.pixelclock = pix_clk; - fmt->bt.standards = V4L2_DV_BT_STD_CVT; - if (reduced_blanking) - fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; - return true; -} -EXPORT_SYMBOL_GPL(v4l2_detect_cvt); - -/* - * GTF defines - * Based on Generalized Timing Formula Standard - * Version 1.1 September 2, 1999 - */ - -#define GTF_PXL_CLK_GRAN 250000 /* pixel clock granularity */ - -#define GTF_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */ -#define GTF_V_FP 1 /* vertical front porch (lines) */ -#define GTF_CELL_GRAN 8 /* character cell granularity */ - -/* Default */ -#define GTF_D_M 600 /* blanking formula gradient */ -#define GTF_D_C 40 /* blanking formula offset */ -#define GTF_D_K 128 /* blanking formula scaling factor */ -#define GTF_D_J 20 /* blanking formula scaling factor */ -#define GTF_D_C_PRIME ((((GTF_D_C - GTF_D_J) * GTF_D_K) / 256) + GTF_D_J) -#define GTF_D_M_PRIME ((GTF_D_K * GTF_D_M) / 256) - -/* Secondary */ -#define GTF_S_M 3600 /* blanking formula gradient */ -#define GTF_S_C 40 /* blanking formula offset */ -#define GTF_S_K 128 /* blanking formula scaling factor */ -#define GTF_S_J 35 /* blanking formula scaling factor */ -#define GTF_S_C_PRIME ((((GTF_S_C - GTF_S_J) * GTF_S_K) / 256) + GTF_S_J) -#define GTF_S_M_PRIME ((GTF_S_K * GTF_S_M) / 256) - -/** v4l2_detect_gtf - detect if the given timings follow the GTF standard - * @frame_height - the total height of the frame (including blanking) in lines. - * @hfreq - the horizontal frequency in Hz. - * @vsync - the height of the vertical sync in lines. - * @polarities - the horizontal and vertical polarities (same as struct - * v4l2_bt_timings polarities). - * @aspect - preferred aspect ratio. GTF has no method of determining the - * aspect ratio in order to derive the image width from the - * image height, so it has to be passed explicitly. Usually - * the native screen aspect ratio is used for this. If it - * is not filled in correctly, then 16:9 will be assumed. - * @fmt - the resulting timings. - * - * This function will attempt to detect if the given values correspond to a - * valid GTF format. If so, then it will return true, and fmt will be filled - * in with the found GTF timings. - */ -bool v4l2_detect_gtf(unsigned frame_height, - unsigned hfreq, - unsigned vsync, - u32 polarities, - struct v4l2_fract aspect, - struct v4l2_dv_timings *fmt) -{ - int pix_clk; - int v_fp, v_bp, h_fp, hsync; - int frame_width, image_height, image_width; - bool default_gtf; - int h_blank; - - if (vsync != 3) - return false; - - if (polarities == V4L2_DV_VSYNC_POS_POL) - default_gtf = true; - else if (polarities == V4L2_DV_HSYNC_POS_POL) - default_gtf = false; - else - return false; - - /* Vertical */ - v_fp = GTF_V_FP; - v_bp = (GTF_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; - image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; - - if (aspect.numerator == 0 || aspect.denominator == 0) { - aspect.numerator = 16; - aspect.denominator = 9; - } - image_width = ((image_height * aspect.numerator) / aspect.denominator); - - /* Horizontal */ - if (default_gtf) - h_blank = ((image_width * GTF_D_C_PRIME * hfreq) - - (image_width * GTF_D_M_PRIME * 1000) + - (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000) / 2) / - (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000); - else - h_blank = ((image_width * GTF_S_C_PRIME * hfreq) - - (image_width * GTF_S_M_PRIME * 1000) + - (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000) / 2) / - (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000); - - h_blank = h_blank - h_blank % (2 * GTF_CELL_GRAN); - frame_width = image_width + h_blank; - - pix_clk = (image_width + h_blank) * hfreq; - pix_clk = pix_clk / GTF_PXL_CLK_GRAN * GTF_PXL_CLK_GRAN; - - hsync = (frame_width * 8 + 50) / 100; - hsync = hsync - hsync % GTF_CELL_GRAN; - - h_fp = h_blank / 2 - hsync; - - fmt->bt.polarities = polarities; - fmt->bt.width = image_width; - fmt->bt.height = image_height; - fmt->bt.hfrontporch = h_fp; - fmt->bt.vfrontporch = v_fp; - fmt->bt.hsync = hsync; - fmt->bt.vsync = vsync; - fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; - fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; - fmt->bt.pixelclock = pix_clk; - fmt->bt.standards = V4L2_DV_BT_STD_GTF; - if (!default_gtf) - fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; - return true; -} -EXPORT_SYMBOL_GPL(v4l2_detect_gtf); - -/** v4l2_calc_aspect_ratio - calculate the aspect ratio based on bytes - * 0x15 and 0x16 from the EDID. - * @hor_landscape - byte 0x15 from the EDID. - * @vert_portrait - byte 0x16 from the EDID. - * - * Determines the aspect ratio from the EDID. - * See VESA Enhanced EDID standard, release A, rev 2, section 3.6.2: - * "Horizontal and Vertical Screen Size or Aspect Ratio" - */ -struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait) -{ - struct v4l2_fract aspect = { 16, 9 }; - u32 tmp; - u8 ratio; - - /* Nothing filled in, fallback to 16:9 */ - if (!hor_landscape && !vert_portrait) - return aspect; - /* Both filled in, so they are interpreted as the screen size in cm */ - if (hor_landscape && vert_portrait) { - aspect.numerator = hor_landscape; - aspect.denominator = vert_portrait; - return aspect; - } - /* Only one is filled in, so interpret them as a ratio: - (val + 99) / 100 */ - ratio = hor_landscape | vert_portrait; - /* Change some rounded values into the exact aspect ratio */ - if (ratio == 79) { - aspect.numerator = 16; - aspect.denominator = 9; - } else if (ratio == 34) { - aspect.numerator = 4; - aspect.numerator = 3; - } else if (ratio == 68) { - aspect.numerator = 15; - aspect.numerator = 9; - } else { - aspect.numerator = hor_landscape + 99; - aspect.denominator = 100; - } - if (hor_landscape) - return aspect; - /* The aspect ratio is for portrait, so swap numerator and denominator */ - tmp = aspect.denominator; - aspect.denominator = aspect.numerator; - aspect.numerator = tmp; - return aspect; -} -EXPORT_SYMBOL_GPL(v4l2_calc_aspect_ratio); - const struct v4l2_frmsize_discrete *v4l2_find_nearest_format( const struct v4l2_discrete_probe *probe, s32 width, s32 height) diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 58279467a7a5..f20b316f7d00 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -24,7 +24,6 @@ #include #include #include -#include #include static const struct v4l2_dv_timings timings[] = { @@ -190,3 +189,360 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, return false; } EXPORT_SYMBOL_GPL(v4l2_find_dv_timings_cap); + +/** + * v4l_match_dv_timings - check if two timings match + * @t1 - compare this v4l2_dv_timings struct... + * @t2 - with this struct. + * @pclock_delta - the allowed pixelclock deviation. + * + * Compare t1 with t2 with a given margin of error for the pixelclock. + */ +bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, + const struct v4l2_dv_timings *t2, + unsigned pclock_delta) +{ + if (t1->type != t2->type || t1->type != V4L2_DV_BT_656_1120) + return false; + if (t1->bt.width == t2->bt.width && + t1->bt.height == t2->bt.height && + t1->bt.interlaced == t2->bt.interlaced && + t1->bt.polarities == t2->bt.polarities && + t1->bt.pixelclock >= t2->bt.pixelclock - pclock_delta && + t1->bt.pixelclock <= t2->bt.pixelclock + pclock_delta && + t1->bt.hfrontporch == t2->bt.hfrontporch && + t1->bt.vfrontporch == t2->bt.vfrontporch && + t1->bt.vsync == t2->bt.vsync && + t1->bt.vbackporch == t2->bt.vbackporch && + (!t1->bt.interlaced || + (t1->bt.il_vfrontporch == t2->bt.il_vfrontporch && + t1->bt.il_vsync == t2->bt.il_vsync && + t1->bt.il_vbackporch == t2->bt.il_vbackporch))) + return true; + return false; +} +EXPORT_SYMBOL_GPL(v4l_match_dv_timings); + +/* + * CVT defines + * Based on Coordinated Video Timings Standard + * version 1.1 September 10, 2003 + */ + +#define CVT_PXL_CLK_GRAN 250000 /* pixel clock granularity */ + +/* Normal blanking */ +#define CVT_MIN_V_BPORCH 7 /* lines */ +#define CVT_MIN_V_PORCH_RND 3 /* lines */ +#define CVT_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */ + +/* Normal blanking for CVT uses GTF to calculate horizontal blanking */ +#define CVT_CELL_GRAN 8 /* character cell granularity */ +#define CVT_M 600 /* blanking formula gradient */ +#define CVT_C 40 /* blanking formula offset */ +#define CVT_K 128 /* blanking formula scaling factor */ +#define CVT_J 20 /* blanking formula scaling factor */ +#define CVT_C_PRIME (((CVT_C - CVT_J) * CVT_K / 256) + CVT_J) +#define CVT_M_PRIME (CVT_K * CVT_M / 256) + +/* Reduced Blanking */ +#define CVT_RB_MIN_V_BPORCH 7 /* lines */ +#define CVT_RB_V_FPORCH 3 /* lines */ +#define CVT_RB_MIN_V_BLANK 460 /* us */ +#define CVT_RB_H_SYNC 32 /* pixels */ +#define CVT_RB_H_BPORCH 80 /* pixels */ +#define CVT_RB_H_BLANK 160 /* pixels */ + +/** v4l2_detect_cvt - detect if the given timings follow the CVT standard + * @frame_height - the total height of the frame (including blanking) in lines. + * @hfreq - the horizontal frequency in Hz. + * @vsync - the height of the vertical sync in lines. + * @polarities - the horizontal and vertical polarities (same as struct + * v4l2_bt_timings polarities). + * @fmt - the resulting timings. + * + * This function will attempt to detect if the given values correspond to a + * valid CVT format. If so, then it will return true, and fmt will be filled + * in with the found CVT timings. + */ +bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, + u32 polarities, struct v4l2_dv_timings *fmt) +{ + int v_fp, v_bp, h_fp, h_bp, hsync; + int frame_width, image_height, image_width; + bool reduced_blanking; + unsigned pix_clk; + + if (vsync < 4 || vsync > 7) + return false; + + if (polarities == V4L2_DV_VSYNC_POS_POL) + reduced_blanking = false; + else if (polarities == V4L2_DV_HSYNC_POS_POL) + reduced_blanking = true; + else + return false; + + /* Vertical */ + if (reduced_blanking) { + v_fp = CVT_RB_V_FPORCH; + v_bp = (CVT_RB_MIN_V_BLANK * hfreq + 999999) / 1000000; + v_bp -= vsync + v_fp; + + if (v_bp < CVT_RB_MIN_V_BPORCH) + v_bp = CVT_RB_MIN_V_BPORCH; + } else { + v_fp = CVT_MIN_V_PORCH_RND; + v_bp = (CVT_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; + + if (v_bp < CVT_MIN_V_BPORCH) + v_bp = CVT_MIN_V_BPORCH; + } + image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; + + /* Aspect ratio based on vsync */ + switch (vsync) { + case 4: + image_width = (image_height * 4) / 3; + break; + case 5: + image_width = (image_height * 16) / 9; + break; + case 6: + image_width = (image_height * 16) / 10; + break; + case 7: + /* special case */ + if (image_height == 1024) + image_width = (image_height * 5) / 4; + else if (image_height == 768) + image_width = (image_height * 15) / 9; + else + return false; + break; + default: + return false; + } + + image_width = image_width & ~7; + + /* Horizontal */ + if (reduced_blanking) { + pix_clk = (image_width + CVT_RB_H_BLANK) * hfreq; + pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; + + h_bp = CVT_RB_H_BPORCH; + hsync = CVT_RB_H_SYNC; + h_fp = CVT_RB_H_BLANK - h_bp - hsync; + + frame_width = image_width + CVT_RB_H_BLANK; + } else { + int h_blank; + unsigned ideal_duty_cycle = CVT_C_PRIME - (CVT_M_PRIME * 1000) / hfreq; + + h_blank = (image_width * ideal_duty_cycle + (100 - ideal_duty_cycle) / 2) / + (100 - ideal_duty_cycle); + h_blank = h_blank - h_blank % (2 * CVT_CELL_GRAN); + + if (h_blank * 100 / image_width < 20) { + h_blank = image_width / 5; + h_blank = (h_blank + 0x7) & ~0x7; + } + + pix_clk = (image_width + h_blank) * hfreq; + pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN; + + h_bp = h_blank / 2; + frame_width = image_width + h_blank; + + hsync = (frame_width * 8 + 50) / 100; + hsync = hsync - hsync % CVT_CELL_GRAN; + h_fp = h_blank - hsync - h_bp; + } + + fmt->bt.polarities = polarities; + fmt->bt.width = image_width; + fmt->bt.height = image_height; + fmt->bt.hfrontporch = h_fp; + fmt->bt.vfrontporch = v_fp; + fmt->bt.hsync = hsync; + fmt->bt.vsync = vsync; + fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; + fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; + fmt->bt.pixelclock = pix_clk; + fmt->bt.standards = V4L2_DV_BT_STD_CVT; + if (reduced_blanking) + fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; + return true; +} +EXPORT_SYMBOL_GPL(v4l2_detect_cvt); + +/* + * GTF defines + * Based on Generalized Timing Formula Standard + * Version 1.1 September 2, 1999 + */ + +#define GTF_PXL_CLK_GRAN 250000 /* pixel clock granularity */ + +#define GTF_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */ +#define GTF_V_FP 1 /* vertical front porch (lines) */ +#define GTF_CELL_GRAN 8 /* character cell granularity */ + +/* Default */ +#define GTF_D_M 600 /* blanking formula gradient */ +#define GTF_D_C 40 /* blanking formula offset */ +#define GTF_D_K 128 /* blanking formula scaling factor */ +#define GTF_D_J 20 /* blanking formula scaling factor */ +#define GTF_D_C_PRIME ((((GTF_D_C - GTF_D_J) * GTF_D_K) / 256) + GTF_D_J) +#define GTF_D_M_PRIME ((GTF_D_K * GTF_D_M) / 256) + +/* Secondary */ +#define GTF_S_M 3600 /* blanking formula gradient */ +#define GTF_S_C 40 /* blanking formula offset */ +#define GTF_S_K 128 /* blanking formula scaling factor */ +#define GTF_S_J 35 /* blanking formula scaling factor */ +#define GTF_S_C_PRIME ((((GTF_S_C - GTF_S_J) * GTF_S_K) / 256) + GTF_S_J) +#define GTF_S_M_PRIME ((GTF_S_K * GTF_S_M) / 256) + +/** v4l2_detect_gtf - detect if the given timings follow the GTF standard + * @frame_height - the total height of the frame (including blanking) in lines. + * @hfreq - the horizontal frequency in Hz. + * @vsync - the height of the vertical sync in lines. + * @polarities - the horizontal and vertical polarities (same as struct + * v4l2_bt_timings polarities). + * @aspect - preferred aspect ratio. GTF has no method of determining the + * aspect ratio in order to derive the image width from the + * image height, so it has to be passed explicitly. Usually + * the native screen aspect ratio is used for this. If it + * is not filled in correctly, then 16:9 will be assumed. + * @fmt - the resulting timings. + * + * This function will attempt to detect if the given values correspond to a + * valid GTF format. If so, then it will return true, and fmt will be filled + * in with the found GTF timings. + */ +bool v4l2_detect_gtf(unsigned frame_height, + unsigned hfreq, + unsigned vsync, + u32 polarities, + struct v4l2_fract aspect, + struct v4l2_dv_timings *fmt) +{ + int pix_clk; + int v_fp, v_bp, h_fp, hsync; + int frame_width, image_height, image_width; + bool default_gtf; + int h_blank; + + if (vsync != 3) + return false; + + if (polarities == V4L2_DV_VSYNC_POS_POL) + default_gtf = true; + else if (polarities == V4L2_DV_HSYNC_POS_POL) + default_gtf = false; + else + return false; + + /* Vertical */ + v_fp = GTF_V_FP; + v_bp = (GTF_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync; + image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1; + + if (aspect.numerator == 0 || aspect.denominator == 0) { + aspect.numerator = 16; + aspect.denominator = 9; + } + image_width = ((image_height * aspect.numerator) / aspect.denominator); + + /* Horizontal */ + if (default_gtf) + h_blank = ((image_width * GTF_D_C_PRIME * hfreq) - + (image_width * GTF_D_M_PRIME * 1000) + + (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000) / 2) / + (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000); + else + h_blank = ((image_width * GTF_S_C_PRIME * hfreq) - + (image_width * GTF_S_M_PRIME * 1000) + + (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000) / 2) / + (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000); + + h_blank = h_blank - h_blank % (2 * GTF_CELL_GRAN); + frame_width = image_width + h_blank; + + pix_clk = (image_width + h_blank) * hfreq; + pix_clk = pix_clk / GTF_PXL_CLK_GRAN * GTF_PXL_CLK_GRAN; + + hsync = (frame_width * 8 + 50) / 100; + hsync = hsync - hsync % GTF_CELL_GRAN; + + h_fp = h_blank / 2 - hsync; + + fmt->bt.polarities = polarities; + fmt->bt.width = image_width; + fmt->bt.height = image_height; + fmt->bt.hfrontporch = h_fp; + fmt->bt.vfrontporch = v_fp; + fmt->bt.hsync = hsync; + fmt->bt.vsync = vsync; + fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync; + fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync; + fmt->bt.pixelclock = pix_clk; + fmt->bt.standards = V4L2_DV_BT_STD_GTF; + if (!default_gtf) + fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING; + return true; +} +EXPORT_SYMBOL_GPL(v4l2_detect_gtf); + +/** v4l2_calc_aspect_ratio - calculate the aspect ratio based on bytes + * 0x15 and 0x16 from the EDID. + * @hor_landscape - byte 0x15 from the EDID. + * @vert_portrait - byte 0x16 from the EDID. + * + * Determines the aspect ratio from the EDID. + * See VESA Enhanced EDID standard, release A, rev 2, section 3.6.2: + * "Horizontal and Vertical Screen Size or Aspect Ratio" + */ +struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait) +{ + struct v4l2_fract aspect = { 16, 9 }; + u32 tmp; + u8 ratio; + + /* Nothing filled in, fallback to 16:9 */ + if (!hor_landscape && !vert_portrait) + return aspect; + /* Both filled in, so they are interpreted as the screen size in cm */ + if (hor_landscape && vert_portrait) { + aspect.numerator = hor_landscape; + aspect.denominator = vert_portrait; + return aspect; + } + /* Only one is filled in, so interpret them as a ratio: + (val + 99) / 100 */ + ratio = hor_landscape | vert_portrait; + /* Change some rounded values into the exact aspect ratio */ + if (ratio == 79) { + aspect.numerator = 16; + aspect.denominator = 9; + } else if (ratio == 34) { + aspect.numerator = 4; + aspect.numerator = 3; + } else if (ratio == 68) { + aspect.numerator = 15; + aspect.numerator = 9; + } else { + aspect.numerator = hor_landscape + 99; + aspect.denominator = 100; + } + if (hor_landscape) + return aspect; + /* The aspect ratio is for portrait, so swap numerator and denominator */ + tmp = aspect.denominator; + aspect.denominator = aspect.numerator; + aspect.numerator = tmp; + return aspect; +} +EXPORT_SYMBOL_GPL(v4l2_calc_aspect_ratio); diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 015ff82da73c..0e1d01056f16 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -201,19 +201,6 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format( const struct v4l2_discrete_probe *probe, s32 width, s32 height); -bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, - const struct v4l2_dv_timings *t2, - unsigned pclock_delta); - -bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, - u32 polarities, struct v4l2_dv_timings *fmt); - -bool v4l2_detect_gtf(unsigned frame_height, unsigned hfreq, unsigned vsync, - u32 polarities, struct v4l2_fract aspect, - struct v4l2_dv_timings *fmt); - -struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait); - void v4l2_get_timestamp(struct timeval *tv); #endif /* V4L2_COMMON_H_ */ diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 41075fa02a96..4c7bb5491658 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -64,4 +64,63 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap, unsigned pclock_delta); +/** v4l_match_dv_timings() - do two timings match? + * @measured: the measured timings data. + * @standard: the timings according to the standard. + * @pclock_delta: maximum delta in Hz between standard->pixelclock and + * the measured timings. + * + * Returns true if the two timings match, returns false otherwise. + */ +bool v4l_match_dv_timings(const struct v4l2_dv_timings *measured, + const struct v4l2_dv_timings *standard, + unsigned pclock_delta); + +/** v4l2_detect_cvt - detect if the given timings follow the CVT standard + * @frame_height - the total height of the frame (including blanking) in lines. + * @hfreq - the horizontal frequency in Hz. + * @vsync - the height of the vertical sync in lines. + * @polarities - the horizontal and vertical polarities (same as struct + * v4l2_bt_timings polarities). + * @fmt - the resulting timings. + * + * This function will attempt to detect if the given values correspond to a + * valid CVT format. If so, then it will return true, and fmt will be filled + * in with the found CVT timings. + */ +bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync, + u32 polarities, struct v4l2_dv_timings *fmt); + +/** v4l2_detect_gtf - detect if the given timings follow the GTF standard + * @frame_height - the total height of the frame (including blanking) in lines. + * @hfreq - the horizontal frequency in Hz. + * @vsync - the height of the vertical sync in lines. + * @polarities - the horizontal and vertical polarities (same as struct + * v4l2_bt_timings polarities). + * @aspect - preferred aspect ratio. GTF has no method of determining the + * aspect ratio in order to derive the image width from the + * image height, so it has to be passed explicitly. Usually + * the native screen aspect ratio is used for this. If it + * is not filled in correctly, then 16:9 will be assumed. + * @fmt - the resulting timings. + * + * This function will attempt to detect if the given values correspond to a + * valid GTF format. If so, then it will return true, and fmt will be filled + * in with the found GTF timings. + */ +bool v4l2_detect_gtf(unsigned frame_height, unsigned hfreq, unsigned vsync, + u32 polarities, struct v4l2_fract aspect, + struct v4l2_dv_timings *fmt); + +/** v4l2_calc_aspect_ratio - calculate the aspect ratio based on bytes + * 0x15 and 0x16 from the EDID. + * @hor_landscape - byte 0x15 from the EDID. + * @vert_portrait - byte 0x16 from the EDID. + * + * Determines the aspect ratio from the EDID. + * See VESA Enhanced EDID standard, release A, rev 2, section 3.6.2: + * "Horizontal and Vertical Screen Size or Aspect Ratio" + */ +struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait); + #endif -- cgit v1.2.3-71-gd317 From 7f68127fa11f08c5468537eb28ca6b8b95d70f08 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jul 2013 08:40:58 -0300 Subject: [media] videodev2.h: defines to calculate blanking and frame sizes It is very common to have to calculate the total width and height of the blanking and the full frame, so add a few defines that deal with that. Signed-off-by: Hans Verkuil Acked-by: Lad, Prabhakar Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index fec0c205f38b..437f1b0f8937 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1057,6 +1057,16 @@ struct v4l2_bt_timings { or used depends on the hardware. */ #define V4L2_DV_FL_HALF_LINE (1 << 3) +/* A few useful defines to calculate the total blanking and frame sizes */ +#define V4L2_DV_BT_BLANKING_WIDTH(bt) \ + (bt->hfrontporch + bt->hsync + bt->hbackporch) +#define V4L2_DV_BT_FRAME_WIDTH(bt) \ + (bt->width + V4L2_DV_BT_BLANKING_WIDTH(bt)) +#define V4L2_DV_BT_BLANKING_HEIGHT(bt) \ + (bt->vfrontporch + bt->vsync + bt->vbackporch + \ + bt->il_vfrontporch + bt->il_vsync + bt->il_vbackporch) +#define V4L2_DV_BT_FRAME_HEIGHT(bt) \ + (bt->height + V4L2_DV_BT_BLANKING_HEIGHT(bt)) /** struct v4l2_dv_timings - DV timings * @type: the type of the timings -- cgit v1.2.3-71-gd317 From 2ccf12afe6da2145085056cebaae2149899f4f8c Mon Sep 17 00:00:00 2001 From: Jon Arne Jørgensen Date: Sat, 3 Aug 2013 09:19:37 -0300 Subject: [media] saa7115: Implement i2c_board_info.platform_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch implements i2c_board_info.platform_data, and some options to override the default initialization table for the GM7113C and SAA7113 chips. Signed-off-by: Jon Arne Jørgensen Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/saa7115.c | 128 ++++++++++++++++++++++++++++++++++++--- drivers/media/i2c/saa711x_regs.h | 15 +++++ include/media/saa7115.h | 64 ++++++++++++++++++++ 3 files changed, 198 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 17a464d4e526..5cc48f751b3a 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -225,19 +225,59 @@ static const unsigned char saa7111_init[] = { 0x00, 0x00 }; -/* SAA7113/GM7113C init codes - * It's important that R_14... R_17 == 0x00 - * for the gm7113c chip to deliver stable video - */ +/* This table has one illegal value, and some values that are not + correct according to the datasheet initialization table. + + If you need a table with legal/default values tell the driver in + i2c_board_info.platform_data, and you will get the gm7113c_init + table instead. */ + +/* SAA7113 Init codes */ static const unsigned char saa7113_init[] = { R_01_INC_DELAY, 0x08, R_02_INPUT_CNTL_1, 0xc2, R_03_INPUT_CNTL_2, 0x30, R_04_INPUT_CNTL_3, 0x00, R_05_INPUT_CNTL_4, 0x00, - R_06_H_SYNC_START, 0x89, + R_06_H_SYNC_START, 0x89, /* Illegal value -119, + * min. value = -108 (0x94) */ + R_07_H_SYNC_STOP, 0x0d, + R_08_SYNC_CNTL, 0x88, /* Not datasheet default. + * HTC = VTR mode, should be 0x98 */ + R_09_LUMA_CNTL, 0x01, + R_0A_LUMA_BRIGHT_CNTL, 0x80, + R_0B_LUMA_CONTRAST_CNTL, 0x47, + R_0C_CHROMA_SAT_CNTL, 0x40, + R_0D_CHROMA_HUE_CNTL, 0x00, + R_0E_CHROMA_CNTL_1, 0x01, + R_0F_CHROMA_GAIN_CNTL, 0x2a, + R_10_CHROMA_CNTL_2, 0x08, /* Not datsheet default. + * VRLN enabled, should be 0x00 */ + R_11_MODE_DELAY_CNTL, 0x0c, + R_12_RT_SIGNAL_CNTL, 0x07, /* Not datasheet default, + * should be 0x01 */ + R_13_RT_X_PORT_OUT_CNTL, 0x00, + R_14_ANAL_ADC_COMPAT_CNTL, 0x00, + R_15_VGATE_START_FID_CHG, 0x00, + R_16_VGATE_STOP, 0x00, + R_17_MISC_VGATE_CONF_AND_MSB, 0x00, + + 0x00, 0x00 +}; + +/* GM7113C is a clone of the SAA7113 chip + This init table is copied out of the saa7113 datasheet. + In R_08 we enable "Automatic Field Detection" [AUFD], + this is disabled when saa711x_set_v4lstd is called. */ +static const unsigned char gm7113c_init[] = { + R_01_INC_DELAY, 0x08, + R_02_INPUT_CNTL_1, 0xc0, + R_03_INPUT_CNTL_2, 0x33, + R_04_INPUT_CNTL_3, 0x00, + R_05_INPUT_CNTL_4, 0x00, + R_06_H_SYNC_START, 0xe9, R_07_H_SYNC_STOP, 0x0d, - R_08_SYNC_CNTL, 0x88, + R_08_SYNC_CNTL, 0x98, R_09_LUMA_CNTL, 0x01, R_0A_LUMA_BRIGHT_CNTL, 0x80, R_0B_LUMA_CONTRAST_CNTL, 0x47, @@ -245,9 +285,9 @@ static const unsigned char saa7113_init[] = { R_0D_CHROMA_HUE_CNTL, 0x00, R_0E_CHROMA_CNTL_1, 0x01, R_0F_CHROMA_GAIN_CNTL, 0x2a, - R_10_CHROMA_CNTL_2, 0x08, + R_10_CHROMA_CNTL_2, 0x00, R_11_MODE_DELAY_CNTL, 0x0c, - R_12_RT_SIGNAL_CNTL, 0x07, + R_12_RT_SIGNAL_CNTL, 0x01, R_13_RT_X_PORT_OUT_CNTL, 0x00, R_14_ANAL_ADC_COMPAT_CNTL, 0x00, R_15_VGATE_START_FID_CHG, 0x00, @@ -1585,6 +1625,65 @@ static const struct v4l2_subdev_ops saa711x_ops = { /* ----------------------------------------------------------------------- */ +static void saa711x_write_platform_data(struct saa711x_state *state, + struct saa7115_platform_data *data) +{ + struct v4l2_subdev *sd = &state->sd; + u8 work; + + if (state->ident != GM7113C && + state->ident != SAA7113) + return; + + if (data->saa7113_r08_htc) { + work = saa711x_read(sd, R_08_SYNC_CNTL); + work &= ~SAA7113_R_08_HTC_MASK; + work |= ((*data->saa7113_r08_htc) << SAA7113_R_08_HTC_OFFSET); + saa711x_write(sd, R_08_SYNC_CNTL, work); + } + + if (data->saa7113_r10_vrln) { + work = saa711x_read(sd, R_10_CHROMA_CNTL_2); + work &= ~SAA7113_R_10_VRLN_MASK; + if (*data->saa7113_r10_vrln) + work |= (1 << SAA7113_R_10_VRLN_OFFSET); + saa711x_write(sd, R_10_CHROMA_CNTL_2, work); + } + + if (data->saa7113_r10_ofts) { + work = saa711x_read(sd, R_10_CHROMA_CNTL_2); + work &= ~SAA7113_R_10_OFTS_MASK; + work |= (*data->saa7113_r10_ofts << SAA7113_R_10_OFTS_OFFSET); + saa711x_write(sd, R_10_CHROMA_CNTL_2, work); + } + + if (data->saa7113_r12_rts0) { + work = saa711x_read(sd, R_12_RT_SIGNAL_CNTL); + work &= ~SAA7113_R_12_RTS0_MASK; + work |= (*data->saa7113_r12_rts0 << SAA7113_R_12_RTS0_OFFSET); + + /* According to the datasheet, + * SAA7113_RTS_DOT_IN should only be used on RTS1 */ + WARN_ON(*data->saa7113_r12_rts0 == SAA7113_RTS_DOT_IN); + saa711x_write(sd, R_12_RT_SIGNAL_CNTL, work); + } + + if (data->saa7113_r12_rts1) { + work = saa711x_read(sd, R_12_RT_SIGNAL_CNTL); + work &= ~SAA7113_R_12_RTS1_MASK; + work |= (*data->saa7113_r12_rts1 << SAA7113_R_12_RTS1_OFFSET); + saa711x_write(sd, R_12_RT_SIGNAL_CNTL, work); + } + + if (data->saa7113_r13_adlsb) { + work = saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL); + work &= ~SAA7113_R_13_ADLSB_MASK; + if (*data->saa7113_r13_adlsb) + work |= (1 << SAA7113_R_13_ADLSB_OFFSET); + saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL, work); + } +} + /** * saa711x_detect_chip - Detects the saa711x (or clone) variant * @client: I2C client structure. @@ -1693,6 +1792,7 @@ static int saa711x_probe(struct i2c_client *client, struct saa711x_state *state; struct v4l2_subdev *sd; struct v4l2_ctrl_handler *hdl; + struct saa7115_platform_data *pdata; int ident; char name[CHIP_VER_SIZE + 1]; @@ -1756,14 +1856,20 @@ static int saa711x_probe(struct i2c_client *client, /* init to 60hz/48khz */ state->crystal_freq = SAA7115_FREQ_24_576_MHZ; + pdata = client->dev.platform_data; switch (state->ident) { case SAA7111: case SAA7111A: saa711x_writeregs(sd, saa7111_init); break; case GM7113C: + saa711x_writeregs(sd, gm7113c_init); + break; case SAA7113: - saa711x_writeregs(sd, saa7113_init); + if (pdata && pdata->saa7113_force_gm7113c_init) + saa711x_writeregs(sd, gm7113c_init); + else + saa711x_writeregs(sd, saa7113_init); break; default: state->crystal_freq = SAA7115_FREQ_32_11_MHZ; @@ -1771,6 +1877,10 @@ static int saa711x_probe(struct i2c_client *client, } if (state->ident > SAA7111A && state->ident != GM7113C) saa711x_writeregs(sd, saa7115_init_misc); + + if (pdata) + saa711x_write_platform_data(state, pdata); + saa711x_set_v4lstd(sd, V4L2_STD_NTSC); v4l2_ctrl_handler_setup(hdl); diff --git a/drivers/media/i2c/saa711x_regs.h b/drivers/media/i2c/saa711x_regs.h index 70c56d19ea06..730ca90b30ac 100644 --- a/drivers/media/i2c/saa711x_regs.h +++ b/drivers/media/i2c/saa711x_regs.h @@ -202,9 +202,24 @@ #define R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES 0xff /* SAA7113 bit-masks */ +#define SAA7113_R_08_HTC_OFFSET 3 +#define SAA7113_R_08_HTC_MASK (0x3 << SAA7113_R_08_HTC_OFFSET) #define SAA7113_R_08_FSEL 0x40 #define SAA7113_R_08_AUFD 0x80 +#define SAA7113_R_10_VRLN_OFFSET 3 +#define SAA7113_R_10_VRLN_MASK (0x1 << SAA7113_R_10_VRLN_OFFSET) +#define SAA7113_R_10_OFTS_OFFSET 6 +#define SAA7113_R_10_OFTS_MASK (0x3 << SAA7113_R_10_OFTS_OFFSET) + +#define SAA7113_R_12_RTS0_OFFSET 0 +#define SAA7113_R_12_RTS0_MASK (0xf << SAA7113_R_12_RTS0_OFFSET) +#define SAA7113_R_12_RTS1_OFFSET 4 +#define SAA7113_R_12_RTS1_MASK (0xf << SAA7113_R_12_RTS1_OFFSET) + +#define SAA7113_R_13_ADLSB_OFFSET 7 +#define SAA7113_R_13_ADLSB_MASK (0x1 << SAA7113_R_13_ADLSB_OFFSET) + #if 0 /* Those structs will be used in the future for debug purposes */ struct saa711x_reg_descr { diff --git a/include/media/saa7115.h b/include/media/saa7115.h index 407918625c80..e8d512a7592f 100644 --- a/include/media/saa7115.h +++ b/include/media/saa7115.h @@ -64,5 +64,69 @@ #define SAA7115_FREQ_FL_APLL (1 << 2) /* SA 3A[3], APLL, SAA7114/5 only */ #define SAA7115_FREQ_FL_DOUBLE_ASCLK (1 << 3) /* SA 39, LRDIV, SAA7114/5 only */ +/* ===== SAA7113 Config enums ===== */ + +/* Register 0x08 "Horizontal time constant" [Bit 3..4]: + * Should be set to "Fast Locking Mode" according to the datasheet, + * and that is the default setting in the gm7113c_init table. + * saa7113_init sets this value to "VTR Mode". */ +enum saa7113_r08_htc { + SAA7113_HTC_TV_MODE = 0x00, + SAA7113_HTC_VTR_MODE, /* Default for saa7113_init */ + SAA7113_HTC_FAST_LOCKING_MODE = 0x03 /* Default for gm7113c_init */ +}; + +/* Register 0x10 "Output format selection" [Bit 6..7]: + * Defaults to ITU_656 as specified in datasheet. */ +enum saa7113_r10_ofts { + SAA7113_OFTS_ITU_656 = 0x0, /* Default */ + SAA7113_OFTS_VFLAG_BY_VREF, + SAA7113_OFTS_VFLAG_BY_DATA_TYPE +}; + +/* Register 0x12 "Output control" [Bit 0..3 Or Bit 4..7]: + * This is used to select what data is output on the RTS0 and RTS1 pins. + * RTS1 [Bit 4..7] Defaults to DOT_IN. (This value can not be set for RTS0) + * RTS0 [Bit 0..3] Defaults to VIPB in gm7113c_init as specified + * in the datasheet, but is set to HREF_HS in the saa7113_init table. */ +enum saa7113_r12_rts { + SAA7113_RTS_DOT_IN = 0, /* OBS: Only for RTS1 (Default RTS1) */ + SAA7113_RTS_VIPB, /* Default RTS0 For gm7113c_init */ + SAA7113_RTS_GPSW, + SAA7115_RTS_HL, + SAA7113_RTS_VL, + SAA7113_RTS_DL, + SAA7113_RTS_PLIN, + SAA7113_RTS_HREF_HS, /* Default RTS0 For saa7113_init */ + SAA7113_RTS_HS, + SAA7113_RTS_HQ, + SAA7113_RTS_ODD, + SAA7113_RTS_VS, + SAA7113_RTS_V123, + SAA7113_RTS_VGATE, + SAA7113_RTS_VREF, + SAA7113_RTS_FID +}; + +struct saa7115_platform_data { + /* saa7113 only: Force the use of the gm7113c_init table, + * instead of the old saa7113_init table. */ + bool saa7113_force_gm7113c_init; + + /* SAA7113/GM7113C Specific configurations */ + enum saa7113_r08_htc *saa7113_r08_htc; /* [R_08 - Bit 3..4] */ + + bool *saa7113_r10_vrln; /* [R_10 - Bit 3] + Disabled for gm7113c_init + Enabled for saa7113c_init */ + enum saa7113_r10_ofts *saa7113_r10_ofts; /* [R_10 - Bit 6..7] */ + + enum saa7113_r12_rts *saa7113_r12_rts0; /* [R_12 - Bit 0..3] */ + enum saa7113_r12_rts *saa7113_r12_rts1; /* [R_12 - Bit 4..7] */ + + bool *saa7113_r13_adlsb; /* [R_13 - Bit 7] + Default disabled */ +}; + #endif -- cgit v1.2.3-71-gd317 From 04074f1fdfe9eefc51bded7f45fafd8cc5d3779c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 18 Aug 2013 08:35:36 -0300 Subject: [media] saa7115: make multi-line comments compliant with CodingStyle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit changeset 2ccf12a did a crappy job when added multi-line comment lines, violating CodingStyle. Change the comments added there to fulfill CodingStyle, and document the platform_data using Documentation/kernel-doc-nano-HOWTO.txt. Cc: Jon Arne Jørgensen Cc: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/saa7115.c | 24 +++++++++++++--------- include/media/saa7115.h | 49 +++++++++++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 5cc48f751b3a..637d02634527 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -225,12 +225,14 @@ static const unsigned char saa7111_init[] = { 0x00, 0x00 }; -/* This table has one illegal value, and some values that are not - correct according to the datasheet initialization table. - - If you need a table with legal/default values tell the driver in - i2c_board_info.platform_data, and you will get the gm7113c_init - table instead. */ +/* + * This table has one illegal value, and some values that are not + * correct according to the datasheet initialization table. + * + * If you need a table with legal/default values tell the driver in + * i2c_board_info.platform_data, and you will get the gm7113c_init + * table instead. + */ /* SAA7113 Init codes */ static const unsigned char saa7113_init[] = { @@ -265,10 +267,12 @@ static const unsigned char saa7113_init[] = { 0x00, 0x00 }; -/* GM7113C is a clone of the SAA7113 chip - This init table is copied out of the saa7113 datasheet. - In R_08 we enable "Automatic Field Detection" [AUFD], - this is disabled when saa711x_set_v4lstd is called. */ +/* + * GM7113C is a clone of the SAA7113 chip + * This init table is copied out of the saa7113 datasheet. + * In R_08 we enable "Automatic Field Detection" [AUFD], + * this is disabled when saa711x_set_v4lstd is called. + */ static const unsigned char gm7113c_init[] = { R_01_INC_DELAY, 0x08, R_02_INPUT_CNTL_1, 0xc0, diff --git a/include/media/saa7115.h b/include/media/saa7115.h index e8d512a7592f..76911e71de17 100644 --- a/include/media/saa7115.h +++ b/include/media/saa7115.h @@ -47,9 +47,11 @@ #define SAA7111_FMT_YUV411 0xc0 /* config flags */ -/* Register 0x85 should set bit 0 to 0 (it's 1 by default). This bit +/* + * Register 0x85 should set bit 0 to 0 (it's 1 by default). This bit * controls the IDQ signal polarity which is set to 'inverted' if the bit - * it 1 and to 'default' if it is 0. */ + * it 1 and to 'default' if it is 0. + */ #define SAA7115_IDQ_IS_DEFAULT (1 << 0) /* s_crystal_freq values and flags */ @@ -84,11 +86,13 @@ enum saa7113_r10_ofts { SAA7113_OFTS_VFLAG_BY_DATA_TYPE }; -/* Register 0x12 "Output control" [Bit 0..3 Or Bit 4..7]: +/* + * Register 0x12 "Output control" [Bit 0..3 Or Bit 4..7]: * This is used to select what data is output on the RTS0 and RTS1 pins. * RTS1 [Bit 4..7] Defaults to DOT_IN. (This value can not be set for RTS0) * RTS0 [Bit 0..3] Defaults to VIPB in gm7113c_init as specified - * in the datasheet, but is set to HREF_HS in the saa7113_init table. */ + * in the datasheet, but is set to HREF_HS in the saa7113_init table. + */ enum saa7113_r12_rts { SAA7113_RTS_DOT_IN = 0, /* OBS: Only for RTS1 (Default RTS1) */ SAA7113_RTS_VIPB, /* Default RTS0 For gm7113c_init */ @@ -108,24 +112,29 @@ enum saa7113_r12_rts { SAA7113_RTS_FID }; +/** + * struct saa7115_platform_data - Allow overriding default initialization + * + * @saa7113_force_gm7113c_init: Force the use of the gm7113c_init table + * instead of saa7113_init table + * (saa7113 only) + * @saa7113_r08_htc: [R_08 - Bit 3..4] + * @saa7113_r10_vrln: [R_10 - Bit 3] + * default: Disabled for gm7113c_init + * Enabled for saa7113c_init + * @saa7113_r10_ofts: [R_10 - Bit 6..7] + * @saa7113_r12_rts0: [R_12 - Bit 0..3] + * @saa7113_r12_rts1: [R_12 - Bit 4..7] + * @saa7113_r13_adlsb: [R_13 - Bit 7] - default: disabled + */ struct saa7115_platform_data { - /* saa7113 only: Force the use of the gm7113c_init table, - * instead of the old saa7113_init table. */ bool saa7113_force_gm7113c_init; - - /* SAA7113/GM7113C Specific configurations */ - enum saa7113_r08_htc *saa7113_r08_htc; /* [R_08 - Bit 3..4] */ - - bool *saa7113_r10_vrln; /* [R_10 - Bit 3] - Disabled for gm7113c_init - Enabled for saa7113c_init */ - enum saa7113_r10_ofts *saa7113_r10_ofts; /* [R_10 - Bit 6..7] */ - - enum saa7113_r12_rts *saa7113_r12_rts0; /* [R_12 - Bit 0..3] */ - enum saa7113_r12_rts *saa7113_r12_rts1; /* [R_12 - Bit 4..7] */ - - bool *saa7113_r13_adlsb; /* [R_13 - Bit 7] - Default disabled */ + enum saa7113_r08_htc *saa7113_r08_htc; + bool *saa7113_r10_vrln; + enum saa7113_r10_ofts *saa7113_r10_ofts; + enum saa7113_r12_rts *saa7113_r12_rts0; + enum saa7113_r12_rts *saa7113_r12_rts1; + bool *saa7113_r13_adlsb; }; #endif -- cgit v1.2.3-71-gd317 From 73135e969970304a474c18c9f732fa3e36d88514 Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Thu, 25 Jul 2013 17:23:10 -0300 Subject: [media] V4L2: soc_camera: Renesas R-Car VIN driver Add Renesas R-Car VIN (Video In) V4L2 driver. Based on the patch by Phil Edworthy . [Sergei: removed deprecated IRQF_DISABLED flag, reordered/renamed 'enum chip_id' values, reordered rcar_vin_id_table[] entries, removed senseless parens from to_buf_list() macro, used ALIGN() macro in rcar_vin_setup(), added {} to the *if* statement and used 'bool' values instead of 0/1 where necessary, removed unused macros, done some reformatting and clarified some comments.] Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/Kconfig | 8 + drivers/media/platform/soc_camera/Makefile | 1 + drivers/media/platform/soc_camera/rcar_vin.c | 1486 ++++++++++++++++++++++++++ include/linux/platform_data/camera-rcar.h | 25 + 4 files changed, 1520 insertions(+) create mode 100644 drivers/media/platform/soc_camera/rcar_vin.c create mode 100644 include/linux/platform_data/camera-rcar.h (limited to 'include') diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 626dcccc37da..af39c4665554 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -44,6 +44,14 @@ config VIDEO_PXA27x ---help--- This is a v4l2 driver for the PXA27x Quick Capture Interface +config VIDEO_RCAR_VIN + tristate "R-Car Video Input (VIN) support" + depends on VIDEO_DEV && SOC_CAMERA + select VIDEOBUF2_DMA_CONTIG + select SOC_CAMERA_SCALE_CROP + ---help--- + This is a v4l2 driver for the R-Car VIN Interface + config VIDEO_SH_MOBILE_CSI2 tristate "SuperH Mobile MIPI CSI-2 Interface driver" depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 39186224c16a..8aed26d7a64d 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o +obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar_vin.o diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c new file mode 100644 index 000000000000..d02a7e0b773f --- /dev/null +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -0,0 +1,1486 @@ +/* + * SoC-camera host driver for Renesas R-Car VIN unit + * + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., + * + * Based on V4L2 Driver for SuperH Mobile CEU interface "sh_mobile_ceu_camera.c" + * + * Copyright (C) 2008 Magnus Damm + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "soc_scale_crop.h" + +#define DRV_NAME "rcar_vin" + +/* Register offsets for R-Car VIN */ +#define VNMC_REG 0x00 /* Video n Main Control Register */ +#define VNMS_REG 0x04 /* Video n Module Status Register */ +#define VNFC_REG 0x08 /* Video n Frame Capture Register */ +#define VNSLPRC_REG 0x0C /* Video n Start Line Pre-Clip Register */ +#define VNELPRC_REG 0x10 /* Video n End Line Pre-Clip Register */ +#define VNSPPRC_REG 0x14 /* Video n Start Pixel Pre-Clip Register */ +#define VNEPPRC_REG 0x18 /* Video n End Pixel Pre-Clip Register */ +#define VNSLPOC_REG 0x1C /* Video n Start Line Post-Clip Register */ +#define VNELPOC_REG 0x20 /* Video n End Line Post-Clip Register */ +#define VNSPPOC_REG 0x24 /* Video n Start Pixel Post-Clip Register */ +#define VNEPPOC_REG 0x28 /* Video n End Pixel Post-Clip Register */ +#define VNIS_REG 0x2C /* Video n Image Stride Register */ +#define VNMB_REG(m) (0x30 + ((m) << 2)) /* Video n Memory Base m Register */ +#define VNIE_REG 0x40 /* Video n Interrupt Enable Register */ +#define VNINTS_REG 0x44 /* Video n Interrupt Status Register */ +#define VNSI_REG 0x48 /* Video n Scanline Interrupt Register */ +#define VNMTC_REG 0x4C /* Video n Memory Transfer Control Register */ +#define VNYS_REG 0x50 /* Video n Y Scale Register */ +#define VNXS_REG 0x54 /* Video n X Scale Register */ +#define VNDMR_REG 0x58 /* Video n Data Mode Register */ +#define VNDMR2_REG 0x5C /* Video n Data Mode Register 2 */ +#define VNUVAOF_REG 0x60 /* Video n UV Address Offset Register */ + +/* Register bit fields for R-Car VIN */ +/* Video n Main Control Register bits */ +#define VNMC_FOC (1 << 21) +#define VNMC_YCAL (1 << 19) +#define VNMC_INF_YUV8_BT656 (0 << 16) +#define VNMC_INF_YUV8_BT601 (1 << 16) +#define VNMC_INF_YUV16 (5 << 16) +#define VNMC_VUP (1 << 10) +#define VNMC_IM_ODD (0 << 3) +#define VNMC_IM_ODD_EVEN (1 << 3) +#define VNMC_IM_EVEN (2 << 3) +#define VNMC_IM_FULL (3 << 3) +#define VNMC_BPS (1 << 1) +#define VNMC_ME (1 << 0) + +/* Video n Module Status Register bits */ +#define VNMS_FBS_MASK (3 << 3) +#define VNMS_FBS_SHIFT 3 +#define VNMS_AV (1 << 1) +#define VNMS_CA (1 << 0) + +/* Video n Frame Capture Register bits */ +#define VNFC_C_FRAME (1 << 1) +#define VNFC_S_FRAME (1 << 0) + +/* Video n Interrupt Enable Register bits */ +#define VNIE_FIE (1 << 4) +#define VNIE_EFE (1 << 1) + +/* Video n Data Mode Register bits */ +#define VNDMR_EXRGB (1 << 8) +#define VNDMR_BPSM (1 << 4) +#define VNDMR_DTMD_YCSEP (1 << 1) +#define VNDMR_DTMD_ARGB1555 (1 << 0) + +/* Video n Data Mode Register 2 bits */ +#define VNDMR2_VPS (1 << 30) +#define VNDMR2_HPS (1 << 29) +#define VNDMR2_FTEV (1 << 17) + +#define VIN_MAX_WIDTH 2048 +#define VIN_MAX_HEIGHT 2048 + +enum chip_id { + RCAR_H1, + RCAR_M1, + RCAR_E1, +}; + +enum rcar_vin_state { + STOPPED = 0, + RUNNING, + STOPPING, +}; + +struct rcar_vin_priv { + void __iomem *base; + spinlock_t lock; + int sequence; + /* State of the VIN module in capturing mode */ + enum rcar_vin_state state; + struct rcar_vin_platform_data *pdata; + struct soc_camera_host ici; + struct list_head capture; +#define MAX_BUFFER_NUM 3 + struct vb2_buffer *queue_buf[MAX_BUFFER_NUM]; + struct vb2_alloc_ctx *alloc_ctx; + enum v4l2_field field; + unsigned int vb_count; + unsigned int nr_hw_slots; + bool request_to_stop; + struct completion capture_stop; + enum chip_id chip; +}; + +#define is_continuous_transfer(priv) (priv->vb_count > MAX_BUFFER_NUM) + +struct rcar_vin_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \ + struct rcar_vin_buffer, \ + vb)->list) + +struct rcar_vin_cam { + /* VIN offsets within the camera output, before the VIN scaler */ + unsigned int vin_left; + unsigned int vin_top; + /* Client output, as seen by the VIN */ + unsigned int width; + unsigned int height; + /* + * User window from S_CROP / G_CROP, produced by client cropping and + * scaling, VIN scaling and VIN cropping, mapped back onto the client + * input window + */ + struct v4l2_rect subrect; + /* Camera cropping rectangle */ + struct v4l2_rect rect; + const struct soc_mbus_pixelfmt *extra_fmt; +}; + +/* + * .queue_setup() is called to check whether the driver can accept the requested + * number of buffers and to fill in plane sizes for the current frame format if + * required + */ +static int rcar_vin_videobuf_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *count, + unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(vq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + + if (fmt) { + const struct soc_camera_format_xlate *xlate; + unsigned int bytes_per_line; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, + fmt->fmt.pix.pixelformat); + if (!xlate) + return -EINVAL; + ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width, + xlate->host_fmt); + if (ret < 0) + return ret; + + bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret); + + ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line, + fmt->fmt.pix.height); + if (ret < 0) + return ret; + + sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret); + } else { + /* Called from VIDIOC_REQBUFS or in compatibility mode */ + sizes[0] = icd->sizeimage; + } + + alloc_ctxs[0] = priv->alloc_ctx; + + if (!vq->num_buffers) + priv->sequence = 0; + + if (!*count) + *count = 2; + priv->vb_count = *count; + + *num_planes = 1; + + /* Number of hardware slots */ + if (is_continuous_transfer(priv)) + priv->nr_hw_slots = MAX_BUFFER_NUM; + else + priv->nr_hw_slots = 1; + + dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]); + + return 0; +} + +static int rcar_vin_setup(struct rcar_vin_priv *priv) +{ + struct soc_camera_device *icd = priv->ici.icd; + struct rcar_vin_cam *cam = icd->host_priv; + u32 vnmc, dmr, interrupts; + bool progressive = false, output_is_yuv = false; + + switch (priv->field) { + case V4L2_FIELD_TOP: + vnmc = VNMC_IM_ODD; + break; + case V4L2_FIELD_BOTTOM: + vnmc = VNMC_IM_EVEN; + break; + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + vnmc = VNMC_IM_FULL; + break; + case V4L2_FIELD_INTERLACED_BT: + vnmc = VNMC_IM_FULL | VNMC_FOC; + break; + case V4L2_FIELD_NONE: + if (is_continuous_transfer(priv)) { + vnmc = VNMC_IM_ODD_EVEN; + progressive = true; + } else { + vnmc = VNMC_IM_ODD; + } + break; + default: + vnmc = VNMC_IM_ODD; + break; + } + + /* input interface */ + switch (icd->current_fmt->code) { + case V4L2_MBUS_FMT_YUYV8_1X16: + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; + break; + case V4L2_MBUS_FMT_YUYV8_2X8: + /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ + vnmc |= priv->pdata->flags & RCAR_VIN_BT656 ? + VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; + default: + break; + } + + /* output format */ + switch (icd->current_fmt->host_fmt->fourcc) { + case V4L2_PIX_FMT_NV16: + iowrite32(ALIGN(cam->width * cam->height, 0x80), + priv->base + VNUVAOF_REG); + dmr = VNDMR_DTMD_YCSEP; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_YUYV: + dmr = VNDMR_BPSM; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_UYVY: + dmr = 0; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_RGB555X: + dmr = VNDMR_DTMD_ARGB1555; + break; + case V4L2_PIX_FMT_RGB565: + dmr = 0; + break; + case V4L2_PIX_FMT_RGB32: + if (priv->chip == RCAR_H1 || priv->chip == RCAR_E1) { + dmr = VNDMR_EXRGB; + break; + } + default: + dev_warn(icd->parent, "Invalid fourcc format (0x%x)\n", + icd->current_fmt->host_fmt->fourcc); + return -EINVAL; + } + + /* Always update on field change */ + vnmc |= VNMC_VUP; + + /* If input and output use the same colorspace, use bypass mode */ + if (output_is_yuv) + vnmc |= VNMC_BPS; + + /* progressive or interlaced mode */ + interrupts = progressive ? VNIE_FIE | VNIE_EFE : VNIE_EFE; + + /* ack interrupts */ + iowrite32(interrupts, priv->base + VNINTS_REG); + /* enable interrupts */ + iowrite32(interrupts, priv->base + VNIE_REG); + /* start capturing */ + iowrite32(dmr, priv->base + VNDMR_REG); + iowrite32(vnmc | VNMC_ME, priv->base + VNMC_REG); + + return 0; +} + +static void rcar_vin_capture(struct rcar_vin_priv *priv) +{ + if (is_continuous_transfer(priv)) + /* Continuous Frame Capture Mode */ + iowrite32(VNFC_C_FRAME, priv->base + VNFC_REG); + else + /* Single Frame Capture Mode */ + iowrite32(VNFC_S_FRAME, priv->base + VNFC_REG); +} + +static void rcar_vin_request_capture_stop(struct rcar_vin_priv *priv) +{ + priv->state = STOPPING; + + /* set continuous & single transfer off */ + iowrite32(0, priv->base + VNFC_REG); + /* disable capture (release DMA buffer), reset */ + iowrite32(ioread32(priv->base + VNMC_REG) & ~VNMC_ME, + priv->base + VNMC_REG); + + /* update the status if stopped already */ + if (!(ioread32(priv->base + VNMS_REG) & VNMS_CA)) + priv->state = STOPPED; +} + +static int rcar_vin_get_free_hw_slot(struct rcar_vin_priv *priv) +{ + int slot; + + for (slot = 0; slot < priv->nr_hw_slots; slot++) + if (priv->queue_buf[slot] == NULL) + return slot; + + return -1; +} + +static int rcar_vin_hw_ready(struct rcar_vin_priv *priv) +{ + /* Ensure all HW slots are filled */ + return rcar_vin_get_free_hw_slot(priv) < 0 ? 1 : 0; +} + +/* Moves a buffer from the queue to the HW slots */ +static int rcar_vin_fill_hw_slot(struct rcar_vin_priv *priv) +{ + struct vb2_buffer *vb; + dma_addr_t phys_addr_top; + int slot; + + if (list_empty(&priv->capture)) + return 0; + + /* Find a free HW slot */ + slot = rcar_vin_get_free_hw_slot(priv); + if (slot < 0) + return 0; + + vb = &list_entry(priv->capture.next, struct rcar_vin_buffer, list)->vb; + list_del_init(to_buf_list(vb)); + priv->queue_buf[slot] = vb; + phys_addr_top = vb2_dma_contig_plane_dma_addr(vb, 0); + iowrite32(phys_addr_top, priv->base + VNMB_REG(slot)); + + return 1; +} + +static void rcar_vin_videobuf_queue(struct vb2_buffer *vb) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + unsigned long size; + + size = icd->sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n", + vb->v4l2_buf.index, vb2_plane_size(vb, 0), size); + goto error; + } + + vb2_set_plane_payload(vb, 0, size); + + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); + + spin_lock_irq(&priv->lock); + + list_add_tail(to_buf_list(vb), &priv->capture); + rcar_vin_fill_hw_slot(priv); + + /* If we weren't running, and have enough buffers, start capturing! */ + if (priv->state != RUNNING && rcar_vin_hw_ready(priv)) { + if (rcar_vin_setup(priv)) { + /* Submit error */ + list_del_init(to_buf_list(vb)); + spin_unlock_irq(&priv->lock); + goto error; + } + priv->request_to_stop = false; + init_completion(&priv->capture_stop); + priv->state = RUNNING; + rcar_vin_capture(priv); + } + + spin_unlock_irq(&priv->lock); + + return; + +error: + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); +} + +static void rcar_vin_videobuf_release(struct vb2_buffer *vb) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + unsigned int i; + int buf_in_use = 0; + + spin_lock_irq(&priv->lock); + + /* Is the buffer in use by the VIN hardware? */ + for (i = 0; i < MAX_BUFFER_NUM; i++) { + if (priv->queue_buf[i] == vb) { + buf_in_use = 1; + break; + } + } + + if (buf_in_use) { + while (priv->state != STOPPED) { + + /* issue stop if running */ + if (priv->state == RUNNING) + rcar_vin_request_capture_stop(priv); + + /* wait until capturing has been stopped */ + if (priv->state == STOPPING) { + priv->request_to_stop = true; + spin_unlock_irq(&priv->lock); + wait_for_completion(&priv->capture_stop); + spin_lock_irq(&priv->lock); + } + } + /* + * Capturing has now stopped. The buffer we have been asked + * to release could be any of the current buffers in use, so + * release all buffers that are in use by HW + */ + for (i = 0; i < MAX_BUFFER_NUM; i++) { + if (priv->queue_buf[i]) { + vb2_buffer_done(priv->queue_buf[i], + VB2_BUF_STATE_ERROR); + priv->queue_buf[i] = NULL; + } + } + } else { + list_del_init(to_buf_list(vb)); + } + + spin_unlock_irq(&priv->lock); +} + +static int rcar_vin_videobuf_init(struct vb2_buffer *vb) +{ + INIT_LIST_HEAD(to_buf_list(vb)); + return 0; +} + +static int rcar_vin_stop_streaming(struct vb2_queue *vq) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(vq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct list_head *buf_head, *tmp; + + spin_lock_irq(&priv->lock); + list_for_each_safe(buf_head, tmp, &priv->capture) + list_del_init(buf_head); + spin_unlock_irq(&priv->lock); + + return 0; +} + +static struct vb2_ops rcar_vin_vb2_ops = { + .queue_setup = rcar_vin_videobuf_setup, + .buf_init = rcar_vin_videobuf_init, + .buf_cleanup = rcar_vin_videobuf_release, + .buf_queue = rcar_vin_videobuf_queue, + .stop_streaming = rcar_vin_stop_streaming, + .wait_prepare = soc_camera_unlock, + .wait_finish = soc_camera_lock, +}; + +static irqreturn_t rcar_vin_irq(int irq, void *data) +{ + struct rcar_vin_priv *priv = data; + u32 int_status; + bool can_run = false, hw_stopped; + int slot; + unsigned int handled = 0; + + spin_lock(&priv->lock); + + int_status = ioread32(priv->base + VNINTS_REG); + if (!int_status) + goto done; + /* ack interrupts */ + iowrite32(int_status, priv->base + VNINTS_REG); + handled = 1; + + /* nothing to do if capture status is 'STOPPED' */ + if (priv->state == STOPPED) + goto done; + + hw_stopped = !(ioread32(priv->base + VNMS_REG) & VNMS_CA); + + if (!priv->request_to_stop) { + if (is_continuous_transfer(priv)) + slot = (ioread32(priv->base + VNMS_REG) & + VNMS_FBS_MASK) >> VNMS_FBS_SHIFT; + else + slot = 0; + + priv->queue_buf[slot]->v4l2_buf.field = priv->field; + priv->queue_buf[slot]->v4l2_buf.sequence = priv->sequence++; + do_gettimeofday(&priv->queue_buf[slot]->v4l2_buf.timestamp); + vb2_buffer_done(priv->queue_buf[slot], VB2_BUF_STATE_DONE); + priv->queue_buf[slot] = NULL; + + if (priv->state != STOPPING) + can_run = rcar_vin_fill_hw_slot(priv); + + if (hw_stopped || !can_run) { + priv->state = STOPPED; + } else if (is_continuous_transfer(priv) && + list_empty(&priv->capture) && + priv->state == RUNNING) { + /* + * The continuous capturing requires an explicit stop + * operation when there is no buffer to be set into + * the VnMBm registers. + */ + rcar_vin_request_capture_stop(priv); + } else { + rcar_vin_capture(priv); + } + + } else if (hw_stopped) { + priv->state = STOPPED; + priv->request_to_stop = false; + complete(&priv->capture_stop); + } + +done: + spin_unlock(&priv->lock); + + return IRQ_RETVAL(handled); +} + +static int rcar_vin_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + int i; + + for (i = 0; i < MAX_BUFFER_NUM; i++) + priv->queue_buf[i] = NULL; + + pm_runtime_get_sync(ici->v4l2_dev.dev); + + dev_dbg(icd->parent, "R-Car VIN driver attached to camera %d\n", + icd->devnum); + + return 0; +} + +static void rcar_vin_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct vb2_buffer *vb; + int i; + + /* disable capture, disable interrupts */ + iowrite32(ioread32(priv->base + VNMC_REG) & ~VNMC_ME, + priv->base + VNMC_REG); + iowrite32(0, priv->base + VNIE_REG); + + priv->state = STOPPED; + priv->request_to_stop = false; + + /* make sure active buffer is cancelled */ + spin_lock_irq(&priv->lock); + for (i = 0; i < MAX_BUFFER_NUM; i++) { + vb = priv->queue_buf[i]; + if (vb) { + list_del_init(to_buf_list(vb)); + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + } + } + spin_unlock_irq(&priv->lock); + + pm_runtime_put(ici->v4l2_dev.dev); + + dev_dbg(icd->parent, "R-Car VIN driver detached from camera %d\n", + icd->devnum); +} + +/* Called with .host_lock held */ +static int rcar_vin_clock_start(struct soc_camera_host *ici) +{ + /* VIN does not have "mclk" */ + return 0; +} + +/* Called with .host_lock held */ +static void rcar_vin_clock_stop(struct soc_camera_host *ici) +{ + /* VIN does not have "mclk" */ +} + +/* rect is guaranteed to not exceed the scaled camera rectangle */ +static int rcar_vin_set_rect(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_cam *cam = icd->host_priv; + struct rcar_vin_priv *priv = ici->priv; + unsigned int left_offset, top_offset; + unsigned char dsize = 0; + struct v4l2_rect *cam_subrect = &cam->subrect; + + dev_dbg(icd->parent, "Crop %ux%u@%u:%u\n", + icd->user_width, icd->user_height, cam->vin_left, cam->vin_top); + + left_offset = cam->vin_left; + top_offset = cam->vin_top; + + if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_RGB32 && + priv->chip == RCAR_E1) + dsize = 1; + + dev_dbg(icd->parent, "Cam %ux%u@%u:%u\n", + cam->width, cam->height, cam->vin_left, cam->vin_top); + dev_dbg(icd->parent, "Cam subrect %ux%u@%u:%u\n", + cam_subrect->width, cam_subrect->height, + cam_subrect->left, cam_subrect->top); + + /* Set Start/End Pixel/Line Pre-Clip */ + iowrite32(left_offset << dsize, priv->base + VNSPPRC_REG); + iowrite32((left_offset + cam->width - 1) << dsize, + priv->base + VNEPPRC_REG); + switch (priv->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + iowrite32(top_offset / 2, priv->base + VNSLPRC_REG); + iowrite32((top_offset + cam->height) / 2 - 1, + priv->base + VNELPRC_REG); + break; + default: + iowrite32(top_offset, priv->base + VNSLPRC_REG); + iowrite32(top_offset + cam->height - 1, + priv->base + VNELPRC_REG); + break; + } + + /* Set Start/End Pixel/Line Post-Clip */ + iowrite32(0, priv->base + VNSPPOC_REG); + iowrite32(0, priv->base + VNSLPOC_REG); + iowrite32((cam_subrect->width - 1) << dsize, priv->base + VNEPPOC_REG); + switch (priv->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + iowrite32(cam_subrect->height / 2 - 1, + priv->base + VNELPOC_REG); + break; + default: + iowrite32(cam_subrect->height - 1, priv->base + VNELPOC_REG); + break; + } + + iowrite32(ALIGN(cam->width, 0x10), priv->base + VNIS_REG); + + return 0; +} + +static void capture_stop_preserve(struct rcar_vin_priv *priv, u32 *vnmc) +{ + *vnmc = ioread32(priv->base + VNMC_REG); + /* module disable */ + iowrite32(*vnmc & ~VNMC_ME, priv->base + VNMC_REG); +} + +static void capture_restore(struct rcar_vin_priv *priv, u32 vnmc) +{ + unsigned long timeout = jiffies + 10 * HZ; + + /* + * Wait until the end of the current frame. It can take a long time, + * but if it has been aborted by a MRST1 reset, it should exit sooner. + */ + while ((ioread32(priv->base + VNMS_REG) & VNMS_AV) && + time_before(jiffies, timeout)) + msleep(1); + + if (time_after(jiffies, timeout)) { + dev_err(priv->ici.v4l2_dev.dev, + "Timeout waiting for frame end! Interface problem?\n"); + return; + } + + iowrite32(vnmc, priv->base + VNMC_REG); +} + +#define VIN_MBUS_FLAGS (V4L2_MBUS_MASTER | \ + V4L2_MBUS_PCLK_SAMPLE_RISING | \ + V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_HSYNC_ACTIVE_LOW | \ + V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_VSYNC_ACTIVE_LOW | \ + V4L2_MBUS_DATA_ACTIVE_HIGH) + +static int rcar_vin_set_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_mbus_config cfg; + unsigned long common_flags; + u32 vnmc; + u32 val; + int ret; + + capture_stop_preserve(priv, &vnmc); + + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, VIN_MBUS_FLAGS); + if (!common_flags) { + dev_warn(icd->parent, + "MBUS flags incompatible: camera 0x%x, host 0x%x\n", + cfg.flags, VIN_MBUS_FLAGS); + return -EINVAL; + } + } else if (ret != -ENOIOCTLCMD) { + return ret; + } else { + common_flags = VIN_MBUS_FLAGS; + } + + /* Make choises, based on platform preferences */ + if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { + if (priv->pdata->flags & RCAR_VIN_HSYNC_ACTIVE_LOW) + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; + else + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; + } + + if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { + if (priv->pdata->flags & RCAR_VIN_VSYNC_ACTIVE_LOW) + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; + else + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; + } + + cfg.flags = common_flags; + ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + val = priv->field == V4L2_FIELD_NONE ? VNDMR2_FTEV : 0; + if (!(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + val |= VNDMR2_VPS; + if (!(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + val |= VNDMR2_HPS; + iowrite32(val, priv->base + VNDMR2_REG); + + ret = rcar_vin_set_rect(icd); + if (ret < 0) + return ret; + + capture_restore(priv, vnmc); + + return 0; +} + +static int rcar_vin_try_bus_param(struct soc_camera_device *icd, + unsigned char buswidth) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_mbus_config cfg; + int ret; + + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (ret == -ENOIOCTLCMD) + return 0; + else if (ret) + return ret; + + if (buswidth > 24) + return -EINVAL; + + /* check is there common mbus flags */ + ret = soc_mbus_config_compatible(&cfg, VIN_MBUS_FLAGS); + if (ret) + return 0; + + dev_warn(icd->parent, + "MBUS flags incompatible: camera 0x%x, host 0x%x\n", + cfg.flags, VIN_MBUS_FLAGS); + + return -EINVAL; +} + +static bool rcar_vin_packing_supported(const struct soc_mbus_pixelfmt *fmt) +{ + return fmt->packing == SOC_MBUS_PACKING_NONE || + (fmt->bits_per_sample > 8 && + fmt->packing == SOC_MBUS_PACKING_EXTEND16); +} + +static const struct soc_mbus_pixelfmt rcar_vin_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV16, + .name = "NV16", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .name = "UYVY", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .name = "RGB565", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, + { + .fourcc = V4L2_PIX_FMT_RGB555X, + .name = "ARGB1555", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, + { + .fourcc = V4L2_PIX_FMT_RGB32, + .name = "RGB888", + .bits_per_sample = 32, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}; + +static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, + struct soc_camera_format_xlate *xlate) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->parent; + int ret, k, n; + int formats = 0; + struct rcar_vin_cam *cam; + enum v4l2_mbus_pixelcode code; + const struct soc_mbus_pixelfmt *fmt; + + ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code); + if (ret < 0) + return 0; + + fmt = soc_mbus_get_fmtdesc(code); + if (!fmt) { + dev_warn(dev, "unsupported format code #%u: %d\n", idx, code); + return 0; + } + + ret = rcar_vin_try_bus_param(icd, fmt->bits_per_sample); + if (ret < 0) + return 0; + + if (!icd->host_priv) { + struct v4l2_mbus_framefmt mf; + struct v4l2_rect rect; + struct device *dev = icd->parent; + int shift; + + ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + if (ret < 0) + return ret; + + /* Cache current client geometry */ + ret = soc_camera_client_g_rect(sd, &rect); + if (ret == -ENOIOCTLCMD) { + /* Sensor driver doesn't support cropping */ + rect.left = 0; + rect.top = 0; + rect.width = mf.width; + rect.height = mf.height; + } else if (ret < 0) { + return ret; + } + + /* + * If sensor proposes too large format then try smaller ones: + * 1280x960, 640x480, 320x240 + */ + for (shift = 0; shift < 3; shift++) { + if (mf.width <= VIN_MAX_WIDTH && + mf.height <= VIN_MAX_HEIGHT) + break; + + mf.width = 1280 >> shift; + mf.height = 960 >> shift; + ret = v4l2_device_call_until_err(sd->v4l2_dev, + soc_camera_grp_id(icd), + video, s_mbus_fmt, + &mf); + if (ret < 0) + return ret; + } + + if (shift == 3) { + dev_err(dev, + "Failed to configure the client below %ux%x\n", + mf.width, mf.height); + return -EIO; + } + + dev_dbg(dev, "camera fmt %ux%u\n", mf.width, mf.height); + + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + if (!cam) + return -ENOMEM; + /* + * We are called with current camera crop, + * initialise subrect with it + */ + cam->rect = rect; + cam->subrect = rect; + cam->width = mf.width; + cam->height = mf.height; + + icd->host_priv = cam; + } else { + cam = icd->host_priv; + } + + /* Beginning of a pass */ + if (!idx) + cam->extra_fmt = NULL; + + switch (code) { + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV8_2X8: + if (cam->extra_fmt) + break; + + /* Add all our formats that can be generated by VIN */ + cam->extra_fmt = rcar_vin_formats; + + n = ARRAY_SIZE(rcar_vin_formats); + formats += n; + for (k = 0; xlate && k < n; k++, xlate++) { + xlate->host_fmt = &rcar_vin_formats[k]; + xlate->code = code; + dev_dbg(dev, "Providing format %s using code %d\n", + rcar_vin_formats[k].name, code); + } + break; + default: + if (!rcar_vin_packing_supported(fmt)) + return 0; + + dev_dbg(dev, "Providing format %s in pass-through mode\n", + fmt->name); + break; + } + + /* Generic pass-through */ + formats++; + if (xlate) { + xlate->host_fmt = fmt; + xlate->code = code; + xlate++; + } + + return formats; +} + +static void rcar_vin_put_formats(struct soc_camera_device *icd) +{ + kfree(icd->host_priv); + icd->host_priv = NULL; +} + +static int rcar_vin_set_crop(struct soc_camera_device *icd, + const struct v4l2_crop *a) +{ + struct v4l2_crop a_writable = *a; + const struct v4l2_rect *rect = &a_writable.c; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct v4l2_crop cam_crop; + struct rcar_vin_cam *cam = icd->host_priv; + struct v4l2_rect *cam_rect = &cam_crop.c; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->parent; + struct v4l2_mbus_framefmt mf; + u32 vnmc; + int ret, i; + + dev_dbg(dev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height, + rect->left, rect->top); + + /* During camera cropping its output window can change too, stop VIN */ + capture_stop_preserve(priv, &vnmc); + dev_dbg(dev, "VNMC_REG 0x%x\n", vnmc); + + /* Apply iterative camera S_CROP for new input window. */ + ret = soc_camera_client_s_crop(sd, &a_writable, &cam_crop, + &cam->rect, &cam->subrect); + if (ret < 0) + return ret; + + dev_dbg(dev, "camera cropped to %ux%u@%u:%u\n", + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top); + + /* On success cam_crop contains current camera crop */ + + /* Retrieve camera output window */ + ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf); + if (ret < 0) + return ret; + + if (mf.width > VIN_MAX_WIDTH || mf.height > VIN_MAX_HEIGHT) + return -EINVAL; + + /* Cache camera output window */ + cam->width = mf.width; + cam->height = mf.height; + + icd->user_width = cam->width; + icd->user_height = cam->height; + + cam->vin_left = rect->left & ~1; + cam->vin_top = rect->top & ~1; + + /* Use VIN cropping to crop to the new window. */ + ret = rcar_vin_set_rect(icd); + if (ret < 0) + return ret; + + cam->subrect = *rect; + + dev_dbg(dev, "VIN cropped to %ux%u@%u:%u\n", + icd->user_width, icd->user_height, + cam->vin_left, cam->vin_top); + + /* Restore capture */ + for (i = 0; i < MAX_BUFFER_NUM; i++) { + if (priv->queue_buf[i] && priv->state == STOPPED) { + vnmc |= VNMC_ME; + break; + } + } + capture_restore(priv, vnmc); + + /* Even if only camera cropping succeeded */ + return ret; +} + +static int rcar_vin_get_crop(struct soc_camera_device *icd, + struct v4l2_crop *a) +{ + struct rcar_vin_cam *cam = icd->host_priv; + + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->c = cam->subrect; + + return 0; +} + +/* Similar to set_crop multistage iterative algorithm */ +static int rcar_vin_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct rcar_vin_cam *cam = icd->host_priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_mbus_framefmt mf; + struct device *dev = icd->parent; + __u32 pixfmt = pix->pixelformat; + const struct soc_camera_format_xlate *xlate; + unsigned int vin_sub_width = 0, vin_sub_height = 0; + int ret; + bool can_scale; + enum v4l2_field field; + v4l2_std_id std; + + dev_dbg(dev, "S_FMT(pix=0x%x, %ux%u)\n", + pixfmt, pix->width, pix->height); + + switch (pix->field) { + default: + pix->field = V4L2_FIELD_NONE; + /* fall-through */ + case V4L2_FIELD_NONE: + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + field = pix->field; + break; + case V4L2_FIELD_INTERLACED: + /* Query for standard if not explicitly mentioned _TB/_BT */ + ret = v4l2_subdev_call(sd, video, querystd, &std); + if (ret < 0) + std = V4L2_STD_625_50; + + field = std & V4L2_STD_625_50 ? V4L2_FIELD_INTERLACED_TB : + V4L2_FIELD_INTERLACED_BT; + break; + } + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(dev, "Format %x not found\n", pixfmt); + return -EINVAL; + } + /* Calculate client output geometry */ + soc_camera_calc_client_output(icd, &cam->rect, &cam->subrect, pix, &mf, + 12); + mf.field = pix->field; + mf.colorspace = pix->colorspace; + mf.code = xlate->code; + + switch (pixfmt) { + case V4L2_PIX_FMT_RGB32: + can_scale = priv->chip != RCAR_E1; + break; + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB555X: + can_scale = true; + break; + default: + can_scale = false; + break; + } + + dev_dbg(dev, "request camera output %ux%u\n", mf.width, mf.height); + + ret = soc_camera_client_scale(icd, &cam->rect, &cam->subrect, + &mf, &vin_sub_width, &vin_sub_height, + can_scale, 12); + + /* Done with the camera. Now see if we can improve the result */ + dev_dbg(dev, "Camera %d fmt %ux%u, requested %ux%u\n", + ret, mf.width, mf.height, pix->width, pix->height); + + if (ret == -ENOIOCTLCMD) + dev_dbg(dev, "Sensor doesn't support scaling\n"); + else if (ret < 0) + return ret; + + if (mf.code != xlate->code) + return -EINVAL; + + /* Prepare VIN crop */ + cam->width = mf.width; + cam->height = mf.height; + + /* Use VIN scaling to scale to the requested user window. */ + + /* We cannot scale up */ + if (pix->width > vin_sub_width) + vin_sub_width = pix->width; + + if (pix->height > vin_sub_height) + vin_sub_height = pix->height; + + pix->colorspace = mf.colorspace; + + if (!can_scale) { + pix->width = vin_sub_width; + pix->height = vin_sub_height; + } + + /* + * We have calculated CFLCR, the actual configuration will be performed + * in rcar_vin_set_bus_param() + */ + + dev_dbg(dev, "W: %u : %u, H: %u : %u\n", + vin_sub_width, pix->width, vin_sub_height, pix->height); + + icd->current_fmt = xlate; + + priv->field = field; + + return 0; +} + +static int rcar_vin_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_mbus_framefmt mf; + __u32 pixfmt = pix->pixelformat; + int width, height; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + xlate = icd->current_fmt; + dev_dbg(icd->parent, "Format %x not found, keeping %x\n", + pixfmt, xlate->host_fmt->fourcc); + pixfmt = xlate->host_fmt->fourcc; + pix->pixelformat = pixfmt; + pix->colorspace = icd->colorspace; + } + + /* FIXME: calculate using depth and bus width */ + v4l_bound_align_image(&pix->width, 2, VIN_MAX_WIDTH, 1, + &pix->height, 4, VIN_MAX_HEIGHT, 2, 0); + + width = pix->width; + height = pix->height; + + /* let soc-camera calculate these values */ + pix->bytesperline = 0; + pix->sizeimage = 0; + + /* limit to sensor capabilities */ + mf.width = pix->width; + mf.height = pix->height; + mf.field = pix->field; + mf.code = xlate->code; + mf.colorspace = pix->colorspace; + + ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd), + video, try_mbus_fmt, &mf); + if (ret < 0) + return ret; + + pix->width = mf.width; + pix->height = mf.height; + pix->field = mf.field; + pix->colorspace = mf.colorspace; + + if (pixfmt == V4L2_PIX_FMT_NV16) { + /* FIXME: check against rect_max after converting soc-camera */ + /* We can scale precisely, need a bigger image from camera */ + if (pix->width < width || pix->height < height) { + /* + * We presume, the sensor behaves sanely, i.e. if + * requested a bigger rectangle, it will not return a + * smaller one. + */ + mf.width = VIN_MAX_WIDTH; + mf.height = VIN_MAX_HEIGHT; + ret = v4l2_device_call_until_err(sd->v4l2_dev, + soc_camera_grp_id(icd), + video, try_mbus_fmt, + &mf); + if (ret < 0) { + dev_err(icd->parent, + "client try_fmt() = %d\n", ret); + return ret; + } + } + /* We will scale exactly */ + if (mf.width > width) + pix->width = width; + if (mf.height > height) + pix->height = height; + } + + return ret; +} + +static unsigned int rcar_vin_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_device *icd = file->private_data; + + return vb2_poll(&icd->vb2_vidq, file, pt); +} + +static int rcar_vin_querycap(struct soc_camera_host *ici, + struct v4l2_capability *cap) +{ + strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + return 0; +} + +static int rcar_vin_init_videobuf2(struct vb2_queue *vq, + struct soc_camera_device *icd) +{ + vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vq->io_modes = VB2_MMAP | VB2_USERPTR; + vq->drv_priv = icd; + vq->ops = &rcar_vin_vb2_ops; + vq->mem_ops = &vb2_dma_contig_memops; + vq->buf_struct_size = sizeof(struct rcar_vin_buffer); + vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + + return vb2_queue_init(vq); +} + +static struct soc_camera_host_ops rcar_vin_host_ops = { + .owner = THIS_MODULE, + .add = rcar_vin_add_device, + .remove = rcar_vin_remove_device, + .clock_start = rcar_vin_clock_start, + .clock_stop = rcar_vin_clock_stop, + .get_formats = rcar_vin_get_formats, + .put_formats = rcar_vin_put_formats, + .get_crop = rcar_vin_get_crop, + .set_crop = rcar_vin_set_crop, + .try_fmt = rcar_vin_try_fmt, + .set_fmt = rcar_vin_set_fmt, + .poll = rcar_vin_poll, + .querycap = rcar_vin_querycap, + .set_bus_param = rcar_vin_set_bus_param, + .init_videobuf2 = rcar_vin_init_videobuf2, +}; + +static struct platform_device_id rcar_vin_id_table[] = { + { "r8a7779-vin", RCAR_H1 }, + { "r8a7778-vin", RCAR_M1 }, + { "uPD35004-vin", RCAR_E1 }, + {}, +}; +MODULE_DEVICE_TABLE(platform, rcar_vin_id_table); + +static int rcar_vin_probe(struct platform_device *pdev) +{ + struct rcar_vin_priv *priv; + struct resource *mem; + struct rcar_vin_platform_data *pdata; + int irq, ret; + + pdata = pdev->dev.platform_data; + if (!pdata || !pdata->flags) { + dev_err(&pdev->dev, "platform data not set\n"); + return -EINVAL; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (mem == NULL) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -EINVAL; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct rcar_vin_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + ret = devm_request_irq(&pdev->dev, irq, rcar_vin_irq, IRQF_SHARED, + dev_name(&pdev->dev), priv); + if (ret) + return ret; + + priv->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(priv->alloc_ctx)) + return PTR_ERR(priv->alloc_ctx); + + priv->ici.priv = priv; + priv->ici.v4l2_dev.dev = &pdev->dev; + priv->ici.nr = pdev->id; + priv->ici.drv_name = dev_name(&pdev->dev); + priv->ici.ops = &rcar_vin_host_ops; + + priv->pdata = pdata; + priv->chip = pdev->id_entry->driver_data; + spin_lock_init(&priv->lock); + INIT_LIST_HEAD(&priv->capture); + + priv->state = STOPPED; + + pm_suspend_ignore_children(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); + + ret = soc_camera_host_register(&priv->ici); + if (ret) + goto cleanup; + + return 0; + +cleanup: + pm_runtime_disable(&pdev->dev); + vb2_dma_contig_cleanup_ctx(priv->alloc_ctx); + + return ret; +} + +static int rcar_vin_remove(struct platform_device *pdev) +{ + struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); + struct rcar_vin_priv *priv = container_of(soc_host, + struct rcar_vin_priv, ici); + + soc_camera_host_unregister(soc_host); + pm_runtime_disable(&pdev->dev); + vb2_dma_contig_cleanup_ctx(priv->alloc_ctx); + + return 0; +} + +static struct platform_driver rcar_vin_driver = { + .probe = rcar_vin_probe, + .remove = rcar_vin_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, + .id_table = rcar_vin_id_table, +}; + +module_platform_driver(rcar_vin_driver); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rcar_vin"); +MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver"); diff --git a/include/linux/platform_data/camera-rcar.h b/include/linux/platform_data/camera-rcar.h new file mode 100644 index 000000000000..dfc83c581593 --- /dev/null +++ b/include/linux/platform_data/camera-rcar.h @@ -0,0 +1,25 @@ +/* + * Platform data for Renesas R-Car VIN soc-camera driver + * + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __CAMERA_RCAR_H_ +#define __CAMERA_RCAR_H_ + +#define RCAR_VIN_HSYNC_ACTIVE_LOW (1 << 0) +#define RCAR_VIN_VSYNC_ACTIVE_LOW (1 << 1) +#define RCAR_VIN_BT601 (1 << 2) +#define RCAR_VIN_BT656 (1 << 3) + +struct rcar_vin_platform_data { + unsigned int flags; +}; + +#endif /* __CAMERA_RCAR_H_ */ -- cgit v1.2.3-71-gd317 From 4dbfd040757b8bf22f4ac17e80b39c068061a16c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 30 Jul 2013 02:59:49 -0300 Subject: [media] V4L2: mx3_camera: add support for asynchronous subdevice registration The soc-camera core does all the work on supporting asynchronous subdevice probing, host drivers only have to pass a subdevice list to soc-camera. Typically this list is provided by the platform. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/soc_camera/mx3_camera.c | 16 +++++++++++++--- include/linux/platform_data/camera-mx3.h | 4 ++++ 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 83592e4ae532..8f9f6211c52e 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -1144,6 +1144,7 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = { static int mx3_camera_probe(struct platform_device *pdev) { + struct mx3_camera_pdata *pdata = pdev->dev.platform_data; struct mx3_camera_dev *mx3_cam; struct resource *res; void __iomem *base; @@ -1155,6 +1156,9 @@ static int mx3_camera_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); + if (!pdata) + return -EINVAL; + mx3_cam = devm_kzalloc(&pdev->dev, sizeof(*mx3_cam), GFP_KERNEL); if (!mx3_cam) { dev_err(&pdev->dev, "Could not allocate mx3 camera object\n"); @@ -1165,8 +1169,8 @@ static int mx3_camera_probe(struct platform_device *pdev) if (IS_ERR(mx3_cam->clk)) return PTR_ERR(mx3_cam->clk); - mx3_cam->pdata = pdev->dev.platform_data; - mx3_cam->platform_flags = mx3_cam->pdata->flags; + mx3_cam->pdata = pdata; + mx3_cam->platform_flags = pdata->flags; if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) { /* * Platform hasn't set available data widths. This is bad. @@ -1185,7 +1189,7 @@ static int mx3_camera_probe(struct platform_device *pdev) if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15) mx3_cam->width_flags |= 1 << 14; - mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000; + mx3_cam->mclk = pdata->mclk_10khz * 10000; if (!mx3_cam->mclk) { dev_warn(&pdev->dev, "mclk_10khz == 0! Please, fix your platform data. " @@ -1210,6 +1214,11 @@ static int mx3_camera_probe(struct platform_device *pdev) if (IS_ERR(mx3_cam->alloc_ctx)) return PTR_ERR(mx3_cam->alloc_ctx); + if (pdata->asd_sizes) { + soc_host->asd = pdata->asd; + soc_host->asd_sizes = pdata->asd_sizes; + } + err = soc_camera_host_register(soc_host); if (err) goto ecamhostreg; @@ -1249,6 +1258,7 @@ static int mx3_camera_remove(struct platform_device *pdev) static struct platform_driver mx3_camera_driver = { .driver = { .name = MX3_CAM_DRV_NAME, + .owner = THIS_MODULE, }, .probe = mx3_camera_probe, .remove = mx3_camera_remove, diff --git a/include/linux/platform_data/camera-mx3.h b/include/linux/platform_data/camera-mx3.h index f226ee3777e1..a910dadc8258 100644 --- a/include/linux/platform_data/camera-mx3.h +++ b/include/linux/platform_data/camera-mx3.h @@ -33,6 +33,8 @@ #define MX3_CAMERA_DATAWIDTH_MASK (MX3_CAMERA_DATAWIDTH_4 | MX3_CAMERA_DATAWIDTH_8 | \ MX3_CAMERA_DATAWIDTH_10 | MX3_CAMERA_DATAWIDTH_15) +struct v4l2_async_subdev; + /** * struct mx3_camera_pdata - i.MX3x camera platform data * @flags: MX3_CAMERA_* flags @@ -43,6 +45,8 @@ struct mx3_camera_pdata { unsigned long flags; unsigned long mclk_10khz; struct device *dma_dev; + struct v4l2_async_subdev **asd; /* Flat array, arranged in groups */ + int *asd_sizes; /* 0-terminated array of asd group sizes */ }; #endif -- cgit v1.2.3-71-gd317 From bfd22c490bc74f9603ea90c37823036660a313e2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 18 Aug 2013 10:17:38 -0300 Subject: v4l2-common: warning fix (W=1): add a missed function prototype changeset d1e9b7c12 added a new ancilliary function to API, but forgot to add it to the corresponding header file. drivers/media/v4l2-core/v4l2-ctrls.c:568:18: warning: no previous prototype for 'v4l2_ctrl_get_int_menu' [-Wmissing-prototypes] const s64 const *v4l2_ctrl_get_int_menu(u32 id, u32 *len) Cc: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-common.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 0e1d01056f16..16550c439008 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -86,6 +86,7 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl, const char * const *menu_items); const char *v4l2_ctrl_get_name(u32 id); const char * const *v4l2_ctrl_get_menu(u32 id); +const s64 const *v4l2_ctrl_get_int_menu(u32 id, u32 *len); int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def); int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl, const char * const *menu_items); -- cgit v1.2.3-71-gd317 From c4c11dd160a8cc98f402c4e12f94b1572e822ffd Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 14 Aug 2013 21:43:30 +0200 Subject: drm/i2c: tda998x: add video and audio input configuration This patch adds tda998x specific parameters to allow it to be configured for different boards using it. Also, this implements rudimentary audio support for S/PDIF attached controllers. Signed-off-by: Russell King Signed-off-by: Sebastian Hesselbarth Tested-by: Darren Etheridge Tested-by: Russell King Tested-by: Russell King Signed-off-by: Dave Airlie --- drivers/gpu/drm/i2c/tda998x_drv.c | 268 ++++++++++++++++++++++++++++++++++++-- include/drm/i2c/tda998x.h | 30 +++++ 2 files changed, 290 insertions(+), 8 deletions(-) create mode 100644 include/drm/i2c/tda998x.h (limited to 'include') diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index 527d11b5ff14..2b64dfa60205 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -23,7 +23,7 @@ #include #include #include - +#include #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) @@ -32,9 +32,11 @@ struct tda998x_priv { uint16_t rev; uint8_t current_page; int dpms; + bool is_hdmi_sink; u8 vip_cntrl_0; u8 vip_cntrl_1; u8 vip_cntrl_2; + struct tda998x_encoder_params params; }; #define to_tda998x_priv(x) ((struct tda998x_priv *)to_encoder_slave(x)->slave_priv) @@ -71,10 +73,13 @@ struct tda998x_priv { # define I2C_MASTER_DIS_MM (1 << 0) # define I2C_MASTER_DIS_FILT (1 << 1) # define I2C_MASTER_APP_STRT_LAT (1 << 2) +#define REG_FEAT_POWERDOWN REG(0x00, 0x0e) /* read/write */ +# define FEAT_POWERDOWN_SPDIF (1 << 3) #define REG_INT_FLAGS_0 REG(0x00, 0x0f) /* read/write */ #define REG_INT_FLAGS_1 REG(0x00, 0x10) /* read/write */ #define REG_INT_FLAGS_2 REG(0x00, 0x11) /* read/write */ # define INT_FLAGS_2_EDID_BLK_RD (1 << 1) +#define REG_ENA_ACLK REG(0x00, 0x16) /* read/write */ #define REG_ENA_VP_0 REG(0x00, 0x18) /* read/write */ #define REG_ENA_VP_1 REG(0x00, 0x19) /* read/write */ #define REG_ENA_VP_2 REG(0x00, 0x1a) /* read/write */ @@ -113,6 +118,7 @@ struct tda998x_priv { #define REG_VIP_CNTRL_5 REG(0x00, 0x25) /* write */ # define VIP_CNTRL_5_CKCASE (1 << 0) # define VIP_CNTRL_5_SP_CNT(x) (((x) & 3) << 1) +#define REG_MUX_AP REG(0x00, 0x26) /* read/write */ #define REG_MUX_VP_VIP_OUT REG(0x00, 0x27) /* read/write */ #define REG_MAT_CONTRL REG(0x00, 0x80) /* write */ # define MAT_CONTRL_MAT_SC(x) (((x) & 3) << 0) @@ -175,6 +181,12 @@ struct tda998x_priv { # define HVF_CNTRL_1_PAD(x) (((x) & 3) << 4) # define HVF_CNTRL_1_SEMI_PLANAR (1 << 6) #define REG_RPT_CNTRL REG(0x00, 0xf0) /* write */ +#define REG_I2S_FORMAT REG(0x00, 0xfc) /* read/write */ +# define I2S_FORMAT(x) (((x) & 3) << 0) +#define REG_AIP_CLKSEL REG(0x00, 0xfd) /* write */ +# define AIP_CLKSEL_FS(x) (((x) & 3) << 0) +# define AIP_CLKSEL_CLK_POL(x) (((x) & 1) << 2) +# define AIP_CLKSEL_AIP(x) (((x) & 7) << 3) /* Page 02h: PLL settings */ @@ -198,6 +210,12 @@ struct tda998x_priv { #define REG_PLL_SCGR1 REG(0x02, 0x09) /* read/write */ #define REG_PLL_SCGR2 REG(0x02, 0x0a) /* read/write */ #define REG_AUDIO_DIV REG(0x02, 0x0e) /* read/write */ +# define AUDIO_DIV_SERCLK_1 0 +# define AUDIO_DIV_SERCLK_2 1 +# define AUDIO_DIV_SERCLK_4 2 +# define AUDIO_DIV_SERCLK_8 3 +# define AUDIO_DIV_SERCLK_16 4 +# define AUDIO_DIV_SERCLK_32 5 #define REG_SEL_CLK REG(0x02, 0x11) /* read/write */ # define SEL_CLK_SEL_CLK1 (1 << 0) # define SEL_CLK_SEL_VRF_CLK(x) (((x) & 3) << 1) @@ -216,6 +234,11 @@ struct tda998x_priv { /* Page 10h: information frames and packets */ +#define REG_IF1_HB0 REG(0x10, 0x20) /* read/write */ +#define REG_IF2_HB0 REG(0x10, 0x40) /* read/write */ +#define REG_IF3_HB0 REG(0x10, 0x60) /* read/write */ +#define REG_IF4_HB0 REG(0x10, 0x80) /* read/write */ +#define REG_IF5_HB0 REG(0x10, 0xa0) /* read/write */ /* Page 11h: audio settings and content info packets */ @@ -225,10 +248,33 @@ struct tda998x_priv { # define AIP_CNTRL_0_LAYOUT (1 << 2) # define AIP_CNTRL_0_ACR_MAN (1 << 5) # define AIP_CNTRL_0_RST_CTS (1 << 6) +#define REG_CA_I2S REG(0x11, 0x01) /* read/write */ +# define CA_I2S_CA_I2S(x) (((x) & 31) << 0) +# define CA_I2S_HBR_CHSTAT (1 << 6) +#define REG_LATENCY_RD REG(0x11, 0x04) /* read/write */ +#define REG_ACR_CTS_0 REG(0x11, 0x05) /* read/write */ +#define REG_ACR_CTS_1 REG(0x11, 0x06) /* read/write */ +#define REG_ACR_CTS_2 REG(0x11, 0x07) /* read/write */ +#define REG_ACR_N_0 REG(0x11, 0x08) /* read/write */ +#define REG_ACR_N_1 REG(0x11, 0x09) /* read/write */ +#define REG_ACR_N_2 REG(0x11, 0x0a) /* read/write */ +#define REG_CTS_N REG(0x11, 0x0c) /* read/write */ +# define CTS_N_K(x) (((x) & 7) << 0) +# define CTS_N_M(x) (((x) & 3) << 4) #define REG_ENC_CNTRL REG(0x11, 0x0d) /* read/write */ # define ENC_CNTRL_RST_ENC (1 << 0) # define ENC_CNTRL_RST_SEL (1 << 1) # define ENC_CNTRL_CTL_CODE(x) (((x) & 3) << 2) +#define REG_DIP_FLAGS REG(0x11, 0x0e) /* read/write */ +# define DIP_FLAGS_ACR (1 << 0) +# define DIP_FLAGS_GC (1 << 1) +#define REG_DIP_IF_FLAGS REG(0x11, 0x0f) /* read/write */ +# define DIP_IF_FLAGS_IF1 (1 << 1) +# define DIP_IF_FLAGS_IF2 (1 << 2) +# define DIP_IF_FLAGS_IF3 (1 << 3) +# define DIP_IF_FLAGS_IF4 (1 << 4) +# define DIP_IF_FLAGS_IF5 (1 << 5) +#define REG_CH_STAT_B(x) REG(0x11, 0x14 + (x)) /* read/write */ /* Page 12h: HDCP and OTP */ @@ -344,6 +390,23 @@ fail: return ret; } +static void +reg_write_range(struct drm_encoder *encoder, uint16_t reg, uint8_t *p, int cnt) +{ + struct i2c_client *client = drm_i2c_encoder_get_client(encoder); + uint8_t buf[cnt+1]; + int ret; + + buf[0] = REG2ADDR(reg); + memcpy(&buf[1], p, cnt); + + set_page(encoder, reg); + + ret = i2c_master_send(client, buf, cnt + 1); + if (ret < 0) + dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg); +} + static uint8_t reg_read(struct drm_encoder *encoder, uint16_t reg) { @@ -412,7 +475,7 @@ tda998x_reset(struct drm_encoder *encoder) reg_write(encoder, REG_SERIALIZER, 0x00); reg_write(encoder, REG_BUFFER_OUT, 0x00); reg_write(encoder, REG_PLL_SCG1, 0x00); - reg_write(encoder, REG_AUDIO_DIV, 0x03); + reg_write(encoder, REG_AUDIO_DIV, AUDIO_DIV_SERCLK_8); reg_write(encoder, REG_SEL_CLK, SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK); reg_write(encoder, REG_PLL_SCGN1, 0xfa); reg_write(encoder, REG_PLL_SCGN2, 0x00); @@ -424,11 +487,184 @@ tda998x_reset(struct drm_encoder *encoder) reg_write(encoder, REG_MUX_VP_VIP_OUT, 0x24); } +static uint8_t tda998x_cksum(uint8_t *buf, size_t bytes) +{ + uint8_t sum = 0; + + while (bytes--) + sum += *buf++; + return (255 - sum) + 1; +} + +#define HB(x) (x) +#define PB(x) (HB(2) + 1 + (x)) + +static void +tda998x_write_if(struct drm_encoder *encoder, uint8_t bit, uint16_t addr, + uint8_t *buf, size_t size) +{ + buf[PB(0)] = tda998x_cksum(buf, size); + + reg_clear(encoder, REG_DIP_IF_FLAGS, bit); + reg_write_range(encoder, addr, buf, size); + reg_set(encoder, REG_DIP_IF_FLAGS, bit); +} + +static void +tda998x_write_aif(struct drm_encoder *encoder, struct tda998x_encoder_params *p) +{ + uint8_t buf[PB(5) + 1]; + + buf[HB(0)] = 0x84; + buf[HB(1)] = 0x01; + buf[HB(2)] = 10; + buf[PB(0)] = 0; + buf[PB(1)] = p->audio_frame[1] & 0x07; /* CC */ + buf[PB(2)] = p->audio_frame[2] & 0x1c; /* SF */ + buf[PB(4)] = p->audio_frame[4]; + buf[PB(5)] = p->audio_frame[5] & 0xf8; /* DM_INH + LSV */ + + tda998x_write_if(encoder, DIP_IF_FLAGS_IF4, REG_IF4_HB0, buf, + sizeof(buf)); +} + +static void +tda998x_write_avi(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + uint8_t buf[PB(13) + 1]; + + memset(buf, 0, sizeof(buf)); + buf[HB(0)] = 0x82; + buf[HB(1)] = 0x02; + buf[HB(2)] = 13; + buf[PB(4)] = drm_match_cea_mode(mode); + + tda998x_write_if(encoder, DIP_IF_FLAGS_IF2, REG_IF2_HB0, buf, + sizeof(buf)); +} + +static void tda998x_audio_mute(struct drm_encoder *encoder, bool on) +{ + if (on) { + reg_set(encoder, REG_SOFTRESET, SOFTRESET_AUDIO); + reg_clear(encoder, REG_SOFTRESET, SOFTRESET_AUDIO); + reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO); + } else { + reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_FIFO); + } +} + +static void +tda998x_configure_audio(struct drm_encoder *encoder, + struct drm_display_mode *mode, struct tda998x_encoder_params *p) +{ + uint8_t buf[6], clksel_aip, clksel_fs, ca_i2s, cts_n, adiv; + uint32_t n; + + /* Enable audio ports */ + reg_write(encoder, REG_ENA_AP, p->audio_cfg); + reg_write(encoder, REG_ENA_ACLK, p->audio_clk_cfg); + + /* Set audio input source */ + switch (p->audio_format) { + case AFMT_SPDIF: + reg_write(encoder, REG_MUX_AP, 0x40); + clksel_aip = AIP_CLKSEL_AIP(0); + /* FS64SPDIF */ + clksel_fs = AIP_CLKSEL_FS(2); + cts_n = CTS_N_M(3) | CTS_N_K(3); + ca_i2s = 0; + break; + + case AFMT_I2S: + reg_write(encoder, REG_MUX_AP, 0x64); + clksel_aip = AIP_CLKSEL_AIP(1); + /* ACLK */ + clksel_fs = AIP_CLKSEL_FS(0); + cts_n = CTS_N_M(3) | CTS_N_K(3); + ca_i2s = CA_I2S_CA_I2S(0); + break; + } + + reg_write(encoder, REG_AIP_CLKSEL, clksel_aip); + reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_LAYOUT); + + /* Enable automatic CTS generation */ + reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_ACR_MAN); + reg_write(encoder, REG_CTS_N, cts_n); + + /* + * Audio input somehow depends on HDMI line rate which is + * related to pixclk. Testing showed that modes with pixclk + * >100MHz need a larger divider while <40MHz need the default. + * There is no detailed info in the datasheet, so we just + * assume 100MHz requires larger divider. + */ + if (mode->clock > 100000) + adiv = AUDIO_DIV_SERCLK_16; + else + adiv = AUDIO_DIV_SERCLK_8; + reg_write(encoder, REG_AUDIO_DIV, adiv); + + /* + * This is the approximate value of N, which happens to be + * the recommended values for non-coherent clocks. + */ + n = 128 * p->audio_sample_rate / 1000; + + /* Write the CTS and N values */ + buf[0] = 0x44; + buf[1] = 0x42; + buf[2] = 0x01; + buf[3] = n; + buf[4] = n >> 8; + buf[5] = n >> 16; + reg_write_range(encoder, REG_ACR_CTS_0, buf, 6); + + /* Set CTS clock reference */ + reg_write(encoder, REG_AIP_CLKSEL, clksel_aip | clksel_fs); + + /* Reset CTS generator */ + reg_set(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS); + reg_clear(encoder, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS); + + /* Write the channel status */ + buf[0] = 0x04; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0xf1; + reg_write_range(encoder, REG_CH_STAT_B(0), buf, 4); + + tda998x_audio_mute(encoder, true); + mdelay(20); + tda998x_audio_mute(encoder, false); + + /* Write the audio information packet */ + tda998x_write_aif(encoder, p); +} + /* DRM encoder functions */ static void tda998x_encoder_set_config(struct drm_encoder *encoder, void *params) { + struct tda998x_priv *priv = to_tda998x_priv(encoder); + struct tda998x_encoder_params *p = params; + + priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) | + (p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) | + VIP_CNTRL_0_SWAP_B(p->swap_b) | + (p->mirr_b ? VIP_CNTRL_0_MIRR_B : 0); + priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(p->swap_c) | + (p->mirr_c ? VIP_CNTRL_1_MIRR_C : 0) | + VIP_CNTRL_1_SWAP_D(p->swap_d) | + (p->mirr_d ? VIP_CNTRL_1_MIRR_D : 0); + priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(p->swap_e) | + (p->mirr_e ? VIP_CNTRL_2_MIRR_E : 0) | + VIP_CNTRL_2_SWAP_F(p->swap_f) | + (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0); + + priv->params = *p; } static void @@ -445,8 +681,7 @@ tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) switch (mode) { case DRM_MODE_DPMS_ON: - /* enable audio and video ports */ - reg_write(encoder, REG_ENA_AP, 0xff); + /* enable video ports, audio will be enabled later */ reg_write(encoder, REG_ENA_VP_0, 0xff); reg_write(encoder, REG_ENA_VP_1, 0xff); reg_write(encoder, REG_ENA_VP_2, 0xff); @@ -608,17 +843,32 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder, reg_write16(encoder, REG_REFPIX_MSB, ref_pix); reg_write16(encoder, REG_REFLINE_MSB, ref_line); - reg = TBG_CNTRL_1_VHX_EXT_DE | - TBG_CNTRL_1_VHX_EXT_HS | - TBG_CNTRL_1_VHX_EXT_VS | - TBG_CNTRL_1_DWIN_DIS | /* HDCP off */ + reg = TBG_CNTRL_1_DWIN_DIS | /* HDCP off */ TBG_CNTRL_1_VH_TGL_2; + /* + * It is questionable whether this is correct - the nxp driver + * does not set VH_TGL_2 and the below for all display modes. + */ if (mode->flags & (DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC)) reg |= TBG_CNTRL_1_VH_TGL_0; reg_set(encoder, REG_TBG_CNTRL_1, reg); /* must be last register set: */ reg_clear(encoder, REG_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE); + + /* Only setup the info frames if the sink is HDMI */ + if (priv->is_hdmi_sink) { + /* We need to turn HDMI HDCP stuff on to get audio through */ + reg_clear(encoder, REG_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS); + reg_write(encoder, REG_ENC_CNTRL, ENC_CNTRL_CTL_CODE(1)); + reg_set(encoder, REG_TX33, TX33_HDMI); + + tda998x_write_avi(encoder, adjusted_mode); + + if (priv->params.audio_cfg) + tda998x_configure_audio(encoder, adjusted_mode, + &priv->params); + } } static enum drm_connector_status @@ -744,12 +994,14 @@ static int tda998x_encoder_get_modes(struct drm_encoder *encoder, struct drm_connector *connector) { + struct tda998x_priv *priv = to_tda998x_priv(encoder); struct edid *edid = (struct edid *)do_get_edid(encoder); int n = 0; if (edid) { drm_mode_connector_update_edid_property(connector, edid); n = drm_add_edid_modes(connector, edid); + priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid); kfree(edid); } diff --git a/include/drm/i2c/tda998x.h b/include/drm/i2c/tda998x.h new file mode 100644 index 000000000000..3e419d92cf5a --- /dev/null +++ b/include/drm/i2c/tda998x.h @@ -0,0 +1,30 @@ +#ifndef __DRM_I2C_TDA998X_H__ +#define __DRM_I2C_TDA998X_H__ + +struct tda998x_encoder_params { + u8 swap_b:3; + u8 mirr_b:1; + u8 swap_a:3; + u8 mirr_a:1; + u8 swap_d:3; + u8 mirr_d:1; + u8 swap_c:3; + u8 mirr_c:1; + u8 swap_f:3; + u8 mirr_f:1; + u8 swap_e:3; + u8 mirr_e:1; + + u8 audio_cfg; + u8 audio_clk_cfg; + u8 audio_frame[6]; + + enum { + AFMT_SPDIF, + AFMT_I2S + } audio_format; + + unsigned audio_sample_rate; +}; + +#endif -- cgit v1.2.3-71-gd317 From 45886af246d926304d5e990da63d55d9db3216c0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Aug 2013 15:41:16 +0200 Subject: drm: kill dev->driver->set_version Totally unused, so just rip it out. Anyway, we want drivers to be fully backwards compatible, allowing them to change behaviour is just a recipe for them to break badly. Reviewed-by: Eric Anholt Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_ioctl.c | 3 --- include/drm/drmP.h | 2 -- 2 files changed, 5 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index ffd7a7ba70d4..0acf0807d1ad 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -352,9 +352,6 @@ int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_pri retcode = -EINVAL; goto done; } - - if (dev->driver->set_version) - dev->driver->set_version(dev, sv); } done: diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 3ecdde6274be..813ca1d11728 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -883,8 +883,6 @@ struct drm_driver { void (*irq_preinstall) (struct drm_device *dev); int (*irq_postinstall) (struct drm_device *dev); void (*irq_uninstall) (struct drm_device *dev); - void (*set_version) (struct drm_device *dev, - struct drm_set_version *sv); /* Master routines */ int (*master_create)(struct drm_device *dev, struct drm_master *master); -- cgit v1.2.3-71-gd317 From 3d914e8357256e7e92d1b7dd2dda9cf94e39c4e8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Aug 2013 15:41:17 +0200 Subject: drm: hide legacy sg cleanup better from common code I've decided that some clear markers for what's legacy dri1/non-gem code is useful. I've opted to use the drm_legacy prefix and then hide all the checks in that function for better readability in the common code. Reviewed-by: Eric Anholt Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_drv.c | 6 +----- drivers/gpu/drm/drm_scatter.c | 10 +++++++++- include/drm/drmP.h | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index dddd79988ffc..743b24d7918e 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -197,11 +197,7 @@ int drm_lastclose(struct drm_device * dev) drm_agp_clear(dev); - if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg && - !drm_core_check_feature(dev, DRIVER_MODESET)) { - drm_sg_cleanup(dev->sg); - dev->sg = NULL; - } + drm_legacy_sg_cleanup(dev); /* Clear vma list (only built for debugging) */ list_for_each_entry_safe(vma, vma_temp, &dev->vmalist, head) { diff --git a/drivers/gpu/drm/drm_scatter.c b/drivers/gpu/drm/drm_scatter.c index a4a076ff1757..dd8a6480065c 100644 --- a/drivers/gpu/drm/drm_scatter.c +++ b/drivers/gpu/drm/drm_scatter.c @@ -46,7 +46,7 @@ static inline void *drm_vmalloc_dma(unsigned long size) #endif } -void drm_sg_cleanup(struct drm_sg_mem * entry) +static void drm_sg_cleanup(struct drm_sg_mem * entry) { struct page *page; int i; @@ -64,6 +64,14 @@ void drm_sg_cleanup(struct drm_sg_mem * entry) kfree(entry); } +void drm_legacy_sg_cleanup(struct drm_device *dev) +{ + if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg && + !drm_core_check_feature(dev, DRIVER_MODESET)) { + drm_sg_cleanup(dev->sg); + dev->sg = NULL; + } +} #ifdef _LP64 # define ScatterHandle(x) (unsigned int)((x >> 32) + (x & ((1L << 32) - 1))) #else diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 813ca1d11728..10d9f83a9df0 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1522,7 +1522,7 @@ extern int drm_vma_info(struct seq_file *m, void *data); #endif /* Scatter Gather Support (drm_scatter.h) */ -extern void drm_sg_cleanup(struct drm_sg_mem * entry); +extern void drm_legacy_sg_cleanup(struct drm_device *dev); extern int drm_sg_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_sg_free(struct drm_device *dev, void *data, -- cgit v1.2.3-71-gd317 From e2e99a8206bcce6f2d3d72ff8be42859f98dbcda Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Aug 2013 15:41:19 +0200 Subject: drm: mark dma setup/teardown as legacy systems And hide the checks a bit better. This was already disallowed for modesetting drivers, so no functinal change here. Reviewed-by: Eric Anholt Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_dma.c | 17 +++++++++++++++-- drivers/gpu/drm/drm_drv.c | 4 +--- drivers/gpu/drm/drm_fops.c | 12 +++--------- include/drm/drmP.h | 4 ++-- 4 files changed, 21 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_dma.c b/drivers/gpu/drm/drm_dma.c index 495b5fd2787c..8a140a953754 100644 --- a/drivers/gpu/drm/drm_dma.c +++ b/drivers/gpu/drm/drm_dma.c @@ -44,10 +44,18 @@ * * Allocate and initialize a drm_device_dma structure. */ -int drm_dma_setup(struct drm_device *dev) +int drm_legacy_dma_setup(struct drm_device *dev) { int i; + if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA) || + drm_core_check_feature(dev, DRIVER_MODESET)) { + return 0; + } + + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + dev->dma = kzalloc(sizeof(*dev->dma), GFP_KERNEL); if (!dev->dma) return -ENOMEM; @@ -66,11 +74,16 @@ int drm_dma_setup(struct drm_device *dev) * Free all pages associated with DMA buffers, the buffers and pages lists, and * finally the drm_device::dma structure itself. */ -void drm_dma_takedown(struct drm_device *dev) +void drm_legacy_dma_takedown(struct drm_device *dev) { struct drm_device_dma *dma = dev->dma; int i, j; + if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA) || + drm_core_check_feature(dev, DRIVER_MODESET)) { + return; + } + if (!dma) return; diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 743b24d7918e..5b949a736712 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -205,9 +205,7 @@ int drm_lastclose(struct drm_device * dev) kfree(vma); } - if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && - !drm_core_check_feature(dev, DRIVER_MODESET)) - drm_dma_takedown(dev); + drm_legacy_dma_takedown(dev); dev->dev_mapping = NULL; mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 72acae908a7d..f343234bd831 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -60,15 +60,9 @@ static int drm_setup(struct drm_device * dev) atomic_set(&dev->ioctl_count, 0); atomic_set(&dev->vma_count, 0); - if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && - !drm_core_check_feature(dev, DRIVER_MODESET)) { - dev->buf_use = 0; - atomic_set(&dev->buf_alloc, 0); - - i = drm_dma_setup(dev); - if (i < 0) - return i; - } + i = drm_legacy_dma_setup(dev); + if (i < 0) + return i; for (i = 0; i < ARRAY_SIZE(dev->counts); i++) atomic_set(&dev->counts[i], 0); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 10d9f83a9df0..1da25304c289 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1374,8 +1374,8 @@ extern int drm_mapbufs(struct drm_device *dev, void *data, struct drm_file *file_priv); /* DMA support (drm_dma.h) */ -extern int drm_dma_setup(struct drm_device *dev); -extern void drm_dma_takedown(struct drm_device *dev); +extern int drm_legacy_dma_setup(struct drm_device *dev); +extern void drm_legacy_dma_takedown(struct drm_device *dev); extern void drm_free_buffer(struct drm_device *dev, struct drm_buf * buf); extern void drm_core_reclaim_buffers(struct drm_device *dev, struct drm_file *filp); -- cgit v1.2.3-71-gd317 From 7c510133d93dd6f15ca040733ba7b2891ed61fd1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Aug 2013 15:41:21 +0200 Subject: drm: mark context support as a legacy subsystem So after a lot of digging around in git histories it looks like this has only ever be used by dri1 render clients. Hence we can fully disable the entire thing for modesetting drivers and so greatly reduce the attack surface for potential exploits (or at least tools like trinity ...). Also add the drm_legacy prefix for functions which are called from common code. To further reduce the impact on common code also extract all the ctx release handling into a function (instead of only releasing individual handles) and make ctxbitmap_cleanup return void - it can never fail. Reviewed-by: Eric Anholt Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_context.c | 73 ++++++++++++++++++++++++++++++++++++++----- drivers/gpu/drm/drm_fops.c | 21 +------------ drivers/gpu/drm/drm_stub.c | 10 ++---- include/drm/drmP.h | 7 +++-- 4 files changed, 72 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c index 224ff965bcf7..b4fb86d89850 100644 --- a/drivers/gpu/drm/drm_context.c +++ b/drivers/gpu/drm/drm_context.c @@ -42,10 +42,6 @@ #include -/******************************************************************/ -/** \name Context bitmap support */ -/*@{*/ - /** * Free a handle from the context bitmap. * @@ -56,13 +52,48 @@ * in drm_device::ctx_idr, while holding the drm_device::struct_mutex * lock. */ -void drm_ctxbitmap_free(struct drm_device * dev, int ctx_handle) +static void drm_ctxbitmap_free(struct drm_device * dev, int ctx_handle) { + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return; + mutex_lock(&dev->struct_mutex); idr_remove(&dev->ctx_idr, ctx_handle); mutex_unlock(&dev->struct_mutex); } +/******************************************************************/ +/** \name Context bitmap support */ +/*@{*/ + +void drm_legacy_ctxbitmap_release(struct drm_device *dev, + struct drm_file *file_priv) +{ + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + mutex_lock(&dev->ctxlist_mutex); + if (!list_empty(&dev->ctxlist)) { + struct drm_ctx_list *pos, *n; + + list_for_each_entry_safe(pos, n, &dev->ctxlist, head) { + if (pos->tag == file_priv && + pos->handle != DRM_KERNEL_CONTEXT) { + if (dev->driver->context_dtor) + dev->driver->context_dtor(dev, + pos->handle); + + drm_ctxbitmap_free(dev, pos->handle); + + list_del(&pos->head); + kfree(pos); + --dev->ctx_count; + } + } + } + mutex_unlock(&dev->ctxlist_mutex); +} + /** * Context bitmap allocation. * @@ -90,10 +121,12 @@ static int drm_ctxbitmap_next(struct drm_device * dev) * * Initialise the drm_device::ctx_idr */ -int drm_ctxbitmap_init(struct drm_device * dev) +void drm_legacy_ctxbitmap_init(struct drm_device * dev) { + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return; + idr_init(&dev->ctx_idr); - return 0; } /** @@ -104,7 +137,7 @@ int drm_ctxbitmap_init(struct drm_device * dev) * Free all idr members using drm_ctx_sarea_free helper function * while holding the drm_device::struct_mutex lock. */ -void drm_ctxbitmap_cleanup(struct drm_device * dev) +void drm_legacy_ctxbitmap_cleanup(struct drm_device * dev) { mutex_lock(&dev->struct_mutex); idr_destroy(&dev->ctx_idr); @@ -136,6 +169,9 @@ int drm_getsareactx(struct drm_device *dev, void *data, struct drm_local_map *map; struct drm_map_list *_entry; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->struct_mutex); map = idr_find(&dev->ctx_idr, request->ctx_id); @@ -180,6 +216,9 @@ int drm_setsareactx(struct drm_device *dev, void *data, struct drm_local_map *map = NULL; struct drm_map_list *r_list = NULL; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + mutex_lock(&dev->struct_mutex); list_for_each_entry(r_list, &dev->maplist, head) { if (r_list->map @@ -280,6 +319,9 @@ int drm_resctx(struct drm_device *dev, void *data, struct drm_ctx ctx; int i; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + if (res->count >= DRM_RESERVED_CONTEXTS) { memset(&ctx, 0, sizeof(ctx)); for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { @@ -310,6 +352,9 @@ int drm_addctx(struct drm_device *dev, void *data, struct drm_ctx_list *ctx_entry; struct drm_ctx *ctx = data; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + ctx->handle = drm_ctxbitmap_next(dev); if (ctx->handle == DRM_KERNEL_CONTEXT) { /* Skip kernel's context and get a new one. */ @@ -353,6 +398,9 @@ int drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_ctx *ctx = data; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + /* This is 0, because we don't handle any context flags */ ctx->flags = 0; @@ -375,6 +423,9 @@ int drm_switchctx(struct drm_device *dev, void *data, { struct drm_ctx *ctx = data; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + DRM_DEBUG("%d\n", ctx->handle); return drm_context_switch(dev, dev->last_context, ctx->handle); } @@ -395,6 +446,9 @@ int drm_newctx(struct drm_device *dev, void *data, { struct drm_ctx *ctx = data; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + DRM_DEBUG("%d\n", ctx->handle); drm_context_switch_complete(dev, file_priv, ctx->handle); @@ -417,6 +471,9 @@ int drm_rmctx(struct drm_device *dev, void *data, { struct drm_ctx *ctx = data; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + DRM_DEBUG("%d\n", ctx->handle); if (ctx->handle != DRM_KERNEL_CONTEXT) { if (dev->driver->context_dtor) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index f343234bd831..10334999f229 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -474,26 +474,7 @@ int drm_release(struct inode *inode, struct file *filp) if (dev->driver->driver_features & DRIVER_GEM) drm_gem_release(dev, file_priv); - mutex_lock(&dev->ctxlist_mutex); - if (!list_empty(&dev->ctxlist)) { - struct drm_ctx_list *pos, *n; - - list_for_each_entry_safe(pos, n, &dev->ctxlist, head) { - if (pos->tag == file_priv && - pos->handle != DRM_KERNEL_CONTEXT) { - if (dev->driver->context_dtor) - dev->driver->context_dtor(dev, - pos->handle); - - drm_ctxbitmap_free(dev, pos->handle); - - list_del(&pos->head); - kfree(pos); - --dev->ctx_count; - } - } - } - mutex_unlock(&dev->ctxlist_mutex); + drm_legacy_ctxbitmap_release(dev, file_priv); mutex_lock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index d663f7d66dab..aa0664d91060 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -288,13 +288,7 @@ int drm_fill_in_dev(struct drm_device *dev, goto error_out_unreg; } - - - retcode = drm_ctxbitmap_init(dev); - if (retcode) { - DRM_ERROR("Cannot allocate memory for context bitmap.\n"); - goto error_out_unreg; - } + drm_legacy_ctxbitmap_init(dev); if (driver->driver_features & DRIVER_GEM) { retcode = drm_gem_init(dev); @@ -463,7 +457,7 @@ void drm_put_dev(struct drm_device *dev) drm_rmmap(dev, r_list->map); drm_ht_remove(&dev->map_hash); - drm_ctxbitmap_cleanup(dev); + drm_legacy_ctxbitmap_cleanup(dev); if (drm_core_check_feature(dev, DRIVER_MODESET)) drm_put_minor(&dev->control); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 1da25304c289..277f307e053d 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1313,9 +1313,10 @@ extern int drm_newctx(struct drm_device *dev, void *data, extern int drm_rmctx(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int drm_ctxbitmap_init(struct drm_device *dev); -extern void drm_ctxbitmap_cleanup(struct drm_device *dev); -extern void drm_ctxbitmap_free(struct drm_device *dev, int ctx_handle); +extern void drm_legacy_ctxbitmap_init(struct drm_device *dev); +extern void drm_legacy_ctxbitmap_cleanup(struct drm_device *dev); +extern void drm_legacy_ctxbitmap_release(struct drm_device *dev, + struct drm_file *file_priv); extern int drm_setsareactx(struct drm_device *dev, void *data, struct drm_file *file_priv); -- cgit v1.2.3-71-gd317 From b0e898ac555e96e7863a5ee95d70f3625f1db5e2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Aug 2013 15:41:23 +0200 Subject: drm: remove FASYNC support So I've stumbled over drm_fasync and wondered what it does. Digging that up is quite a story. First I've had to read up on what this does and ended up being rather bewildered why peopled loved signals so much back in the days that they've created SIGIO just for that ... Then I wondered how this ever works, and what that strange "No-op." comment right above it should mean. After all calling the core fasync helper is pretty obviously not a noop. After reading through the kernels FASYNC implementation I've noticed that signals are only sent out to the processes attached with FASYNC by calling kill_fasync. No merged drm driver has ever done that. After more digging I've found out that the only driver that ever used this is the so called GAMMA driver. I've frankly never heard of such a gpu brand ever before. Now FASYNC seems to not have been the only bad thing with that driver, since Dave Airlie removed it from the drm driver with prejudice: commit 1430163b4bbf7b00367ea1066c1c5fe85dbeefed Author: Dave Airlie Date: Sun Aug 29 12:04:35 2004 +0000 Drop GAMMA DRM from a great height ... Long story short, the drm fasync support seems to be doing absolutely nothing. And the only user of it was never merged into the upstream kernel. And we don't need any fops->fasync callback since the fcntl implementation in the kernel already implements the noop case correctly. So stop this particular cargo-cult and rip it all out. v2: Kill drm_fasync assignments in rcar (newly added) and imx drivers (somehow I've missed that one in staging). Also drop the reference in the drm DocBook. ARM compile-fail reported by Rob Clark. v3: Move the removal of dev->buf_asnyc assignment in drm_setup to this patch here. v4: Actually git add ... tsk. Cc: Dave Airlie Cc: Laurent Pinchart Cc: Rob Clark Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter Reviewed-by: David Herrmann Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 1 - drivers/gpu/drm/ast/ast_drv.c | 1 - drivers/gpu/drm/cirrus/cirrus_drv.c | 1 - drivers/gpu/drm/drm_fops.c | 14 -------------- drivers/gpu/drm/gma500/psb_drv.c | 1 - drivers/gpu/drm/i810/i810_dma.c | 1 - drivers/gpu/drm/i810/i810_drv.c | 1 - drivers/gpu/drm/i915/i915_drv.c | 1 - drivers/gpu/drm/mga/mga_drv.c | 1 - drivers/gpu/drm/mgag200/mgag200_drv.c | 1 - drivers/gpu/drm/nouveau/nouveau_drm.c | 1 - drivers/gpu/drm/omapdrm/omap_drv.c | 1 - drivers/gpu/drm/qxl/qxl_drv.c | 1 - drivers/gpu/drm/r128/r128_drv.c | 1 - drivers/gpu/drm/radeon/radeon_drv.c | 2 -- drivers/gpu/drm/rcar-du/rcar_du_drv.c | 1 - drivers/gpu/drm/savage/savage_drv.c | 1 - drivers/gpu/drm/shmobile/shmob_drm_drv.c | 1 - drivers/gpu/drm/sis/sis_drv.c | 1 - drivers/gpu/drm/tdfx/tdfx_drv.c | 1 - drivers/gpu/drm/tilcdc/tilcdc_drv.c | 1 - drivers/gpu/drm/udl/udl_drv.c | 1 - drivers/gpu/drm/via/via_drv.c | 1 - drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 1 - drivers/gpu/host1x/drm/drm.c | 1 - drivers/staging/imx-drm/imx-drm-core.c | 1 - include/drm/drmP.h | 3 --- 27 files changed, 43 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 87e22ecd9281..9494ab8af9e7 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2504,7 +2504,6 @@ void (*postclose) (struct drm_device *, struct drm_file *); .poll = drm_poll, .read = drm_read, - .fasync = drm_fasync, .llseek = no_llseek, diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index a144fb044852..60f1ce3998c3 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -190,7 +190,6 @@ static const struct file_operations ast_fops = { .unlocked_ioctl = drm_ioctl, .mmap = ast_mmap, .poll = drm_poll, - .fasync = drm_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = drm_compat_ioctl, #endif diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index d35d99c15f84..dd9c908ab3fc 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -85,7 +85,6 @@ static const struct file_operations cirrus_driver_fops = { #ifdef CONFIG_COMPAT .compat_ioctl = drm_compat_ioctl, #endif - .fasync = drm_fasync, }; static struct drm_driver driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_USE_MTRR, diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 10334999f229..1817f03efe80 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -73,8 +73,6 @@ static int drm_setup(struct drm_device * dev) dev->last_context = 0; dev->if_version = 0; - dev->buf_async = NULL; - DRM_DEBUG("\n"); /* @@ -372,18 +370,6 @@ out_put_pid: return ret; } -/** No-op. */ -int drm_fasync(int fd, struct file *filp, int on) -{ - struct drm_file *priv = filp->private_data; - struct drm_device *dev = priv->minor->dev; - - DRM_DEBUG("fd = %d, device = 0x%lx\n", fd, - (long)old_encode_dev(priv->minor->device)); - return fasync_helper(fd, filp, on, &dev->buf_async); -} -EXPORT_SYMBOL(drm_fasync); - static void drm_master_release(struct drm_device *dev, struct file *filp) { struct drm_file *file_priv = filp->private_data; diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index d13c2fc848bc..99b5293972c6 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -622,7 +622,6 @@ static const struct file_operations psb_gem_fops = { .unlocked_ioctl = psb_unlocked_ioctl, .mmap = drm_gem_mmap, .poll = drm_poll, - .fasync = drm_fasync, .read = drm_read, }; diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c index eac755bb8f9b..ab1892eb1074 100644 --- a/drivers/gpu/drm/i810/i810_dma.c +++ b/drivers/gpu/drm/i810/i810_dma.c @@ -113,7 +113,6 @@ static const struct file_operations i810_buffer_fops = { .release = drm_release, .unlocked_ioctl = drm_ioctl, .mmap = i810_mmap_buffers, - .fasync = drm_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = drm_compat_ioctl, #endif diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c index 2e91fc3580b4..d85c05b4877d 100644 --- a/drivers/gpu/drm/i810/i810_drv.c +++ b/drivers/gpu/drm/i810/i810_drv.c @@ -49,7 +49,6 @@ static const struct file_operations i810_driver_fops = { .unlocked_ioctl = drm_ioctl, .mmap = drm_mmap, .poll = drm_poll, - .fasync = drm_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = drm_compat_ioctl, #endif diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 13457e3e9cad..9411a745adaf 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -994,7 +994,6 @@ static const struct file_operations i915_driver_fops = { .unlocked_ioctl = drm_ioctl, .mmap = drm_gem_mmap, .poll = drm_poll, - .fasync = drm_fasync, .read = drm_read, #ifdef CONFIG_COMPAT .compat_ioctl = i915_compat_ioctl, diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c index 17d0a637e4fb..fe71e1e44e48 100644 --- a/drivers/gpu/drm/mga/mga_drv.c +++ b/drivers/gpu/drm/mga/mga_drv.c @@ -50,7 +50,6 @@ static const struct file_operations mga_driver_fops = { .unlocked_ioctl = drm_ioctl, .mmap = drm_mmap, .poll = drm_poll, - .fasync = drm_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = mga_compat_ioctl, #endif diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index bd9196478735..b570127ae3b2 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -81,7 +81,6 @@ static const struct file_operations mgag200_driver_fops = { .unlocked_ioctl = drm_ioctl, .mmap = mgag200_mmap, .poll = drm_poll, - .fasync = drm_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = drm_compat_ioctl, #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 1faa75f42393..b29d04b822ae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -673,7 +673,6 @@ nouveau_driver_fops = { .unlocked_ioctl = drm_ioctl, .mmap = nouveau_ttm_mmap, .poll = drm_poll, - .fasync = drm_fasync, .read = drm_read, #if defined(CONFIG_COMPAT) .compat_ioctl = nouveau_compat_ioctl, diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 47e64f916254..2603d909f49c 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -592,7 +592,6 @@ static const struct file_operations omapdriver_fops = { .release = drm_release, .mmap = omap_gem_mmap, .poll = drm_poll, - .fasync = drm_fasync, .read = drm_read, .llseek = noop_llseek, }; diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index 48f2dfdeabcb..514118ae72d4 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -84,7 +84,6 @@ static const struct file_operations qxl_fops = { .release = drm_release, .unlocked_ioctl = drm_ioctl, .poll = drm_poll, - .fasync = drm_fasync, .mmap = qxl_mmap, }; diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c index 472c38fe123f..c2338cbc56ad 100644 --- a/drivers/gpu/drm/r128/r128_drv.c +++ b/drivers/gpu/drm/r128/r128_drv.c @@ -48,7 +48,6 @@ static const struct file_operations r128_driver_fops = { .unlocked_ioctl = drm_ioctl, .mmap = drm_mmap, .poll = drm_poll, - .fasync = drm_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = r128_compat_ioctl, #endif diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 3585f22c5c15..3e52331124de 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -266,7 +266,6 @@ static const struct file_operations radeon_driver_old_fops = { .unlocked_ioctl = drm_ioctl, .mmap = drm_mmap, .poll = drm_poll, - .fasync = drm_fasync, .read = drm_read, #ifdef CONFIG_COMPAT .compat_ioctl = radeon_compat_ioctl, @@ -375,7 +374,6 @@ static const struct file_operations radeon_driver_kms_fops = { .unlocked_ioctl = drm_ioctl, .mmap = radeon_mmap, .poll = drm_poll, - .fasync = drm_fasync, .read = drm_read, #ifdef CONFIG_COMPAT .compat_ioctl = radeon_kms_compat_ioctl, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 0a9f1bb88337..0023f9719cf1 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -148,7 +148,6 @@ static const struct file_operations rcar_du_fops = { #endif .poll = drm_poll, .read = drm_read, - .fasync = drm_fasync, .llseek = no_llseek, .mmap = drm_gem_cma_mmap, }; diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c index 71b2081e7835..9135c8bd6fbc 100644 --- a/drivers/gpu/drm/savage/savage_drv.c +++ b/drivers/gpu/drm/savage/savage_drv.c @@ -42,7 +42,6 @@ static const struct file_operations savage_driver_fops = { .unlocked_ioctl = drm_ioctl, .mmap = drm_mmap, .poll = drm_poll, - .fasync = drm_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = drm_compat_ioctl, #endif diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index 7f2ea1a5a45f..015551866b4a 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -257,7 +257,6 @@ static const struct file_operations shmob_drm_fops = { #endif .poll = drm_poll, .read = drm_read, - .fasync = drm_fasync, .llseek = no_llseek, .mmap = drm_gem_cma_mmap, }; diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c index 5a5325e6b759..b88b2d302105 100644 --- a/drivers/gpu/drm/sis/sis_drv.c +++ b/drivers/gpu/drm/sis/sis_drv.c @@ -72,7 +72,6 @@ static const struct file_operations sis_driver_fops = { .unlocked_ioctl = drm_ioctl, .mmap = drm_mmap, .poll = drm_poll, - .fasync = drm_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = drm_compat_ioctl, #endif diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c index ddfa743459d0..951ec13e4e5c 100644 --- a/drivers/gpu/drm/tdfx/tdfx_drv.c +++ b/drivers/gpu/drm/tdfx/tdfx_drv.c @@ -48,7 +48,6 @@ static const struct file_operations tdfx_driver_fops = { .unlocked_ioctl = drm_ioctl, .mmap = drm_mmap, .poll = drm_poll, - .fasync = drm_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = drm_compat_ioctl, #endif diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 14801c2235ae..116da199b942 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -497,7 +497,6 @@ static const struct file_operations fops = { #endif .poll = drm_poll, .read = drm_read, - .fasync = drm_fasync, .llseek = no_llseek, .mmap = drm_gem_cma_mmap, }; diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index bb0af58c769a..7650dc0d78ce 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -65,7 +65,6 @@ static const struct file_operations udl_driver_fops = { .read = drm_read, .unlocked_ioctl = drm_ioctl, .release = drm_release, - .fasync = drm_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = drm_compat_ioctl, #endif diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c index f4ae20327941..448799968a06 100644 --- a/drivers/gpu/drm/via/via_drv.c +++ b/drivers/gpu/drm/via/via_drv.c @@ -64,7 +64,6 @@ static const struct file_operations via_driver_fops = { .unlocked_ioctl = drm_ioctl, .mmap = drm_mmap, .poll = drm_poll, - .fasync = drm_fasync, #ifdef CONFIG_COMPAT .compat_ioctl = drm_compat_ioctl, #endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 81ef6bac8737..2dd1919485e4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -1120,7 +1120,6 @@ static const struct file_operations vmwgfx_driver_fops = { .mmap = vmw_mmap, .poll = vmw_fops_poll, .read = vmw_fops_read, - .fasync = drm_fasync, #if defined(CONFIG_COMPAT) .compat_ioctl = drm_compat_ioctl, #endif diff --git a/drivers/gpu/host1x/drm/drm.c b/drivers/gpu/host1x/drm/drm.c index b128b90a94f6..15684bf073fb 100644 --- a/drivers/gpu/host1x/drm/drm.c +++ b/drivers/gpu/host1x/drm/drm.c @@ -508,7 +508,6 @@ static const struct file_operations tegra_drm_fops = { .unlocked_ioctl = drm_ioctl, .mmap = tegra_drm_mmap, .poll = drm_poll, - .fasync = drm_fasync, .read = drm_read, #ifdef CONFIG_COMPAT .compat_ioctl = drm_compat_ioctl, diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index a8900496b980..29607c25b261 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -207,7 +207,6 @@ static const struct file_operations imx_drm_driver_fops = { .unlocked_ioctl = drm_ioctl, .mmap = drm_gem_cma_mmap, .poll = drm_poll, - .fasync = drm_fasync, .read = drm_read, .llseek = noop_llseek, }; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 277f307e053d..cef9a507246e 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1166,8 +1166,6 @@ struct drm_device { /*@} */ - struct fasync_struct *buf_async;/**< Processes waiting for SIGIO */ - struct drm_agp_head *agp; /**< AGP data */ struct device *dev; /**< Device structure */ @@ -1264,7 +1262,6 @@ extern int drm_lastclose(struct drm_device *dev); extern struct mutex drm_global_mutex; extern int drm_open(struct inode *inode, struct file *filp); extern int drm_stub_open(struct inode *inode, struct file *filp); -extern int drm_fasync(int fd, struct file *filp, int on); extern ssize_t drm_read(struct file *filp, char __user *buffer, size_t count, loff_t *offset); extern int drm_release(struct inode *inode, struct file *filp); -- cgit v1.2.3-71-gd317 From 687fbb2e4f46ad00cbfef3f0da2425af7e8684a1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Aug 2013 15:41:24 +0200 Subject: drm: rip out DRIVER_FB_DMA and related code No driver ever sets that flag, so good riddance! Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_bufs.c | 161 +-------------------------------------------- include/drm/drmP.h | 1 - 2 files changed, 2 insertions(+), 160 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index cccc25f7ef86..5f73f0af6125 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -1130,161 +1130,6 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request return 0; } -static int drm_addbufs_fb(struct drm_device * dev, struct drm_buf_desc * request) -{ - struct drm_device_dma *dma = dev->dma; - struct drm_buf_entry *entry; - struct drm_buf *buf; - unsigned long offset; - unsigned long agp_offset; - int count; - int order; - int size; - int alignment; - int page_order; - int total; - int byte_count; - int i; - struct drm_buf **temp_buflist; - - if (!drm_core_check_feature(dev, DRIVER_FB_DMA)) - return -EINVAL; - - if (!dma) - return -EINVAL; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - count = request->count; - order = order_base_2(request->size); - size = 1 << order; - - alignment = (request->flags & _DRM_PAGE_ALIGN) - ? PAGE_ALIGN(size) : size; - page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; - total = PAGE_SIZE << page_order; - - byte_count = 0; - agp_offset = request->agp_start; - - DRM_DEBUG("count: %d\n", count); - DRM_DEBUG("order: %d\n", order); - DRM_DEBUG("size: %d\n", size); - DRM_DEBUG("agp_offset: %lu\n", agp_offset); - DRM_DEBUG("alignment: %d\n", alignment); - DRM_DEBUG("page_order: %d\n", page_order); - DRM_DEBUG("total: %d\n", total); - - if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) - return -EINVAL; - - spin_lock(&dev->count_lock); - if (dev->buf_use) { - spin_unlock(&dev->count_lock); - return -EBUSY; - } - atomic_inc(&dev->buf_alloc); - spin_unlock(&dev->count_lock); - - mutex_lock(&dev->struct_mutex); - entry = &dma->bufs[order]; - if (entry->buf_count) { - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; /* May only call once for each order */ - } - - if (count < 0 || count > 4096) { - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -EINVAL; - } - - entry->buflist = kzalloc(count * sizeof(*entry->buflist), - GFP_KERNEL); - if (!entry->buflist) { - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - - entry->buf_size = size; - entry->page_order = page_order; - - offset = 0; - - while (entry->buf_count < count) { - buf = &entry->buflist[entry->buf_count]; - buf->idx = dma->buf_count + entry->buf_count; - buf->total = alignment; - buf->order = order; - buf->used = 0; - - buf->offset = (dma->byte_count + offset); - buf->bus_address = agp_offset + offset; - buf->address = (void *)(agp_offset + offset); - buf->next = NULL; - buf->waiting = 0; - buf->pending = 0; - buf->file_priv = NULL; - - buf->dev_priv_size = dev->driver->dev_priv_size; - buf->dev_private = kzalloc(buf->dev_priv_size, GFP_KERNEL); - if (!buf->dev_private) { - /* Set count correctly so we free the proper amount. */ - entry->buf_count = count; - drm_cleanup_buf_error(dev, entry); - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - - DRM_DEBUG("buffer %d @ %p\n", entry->buf_count, buf->address); - - offset += alignment; - entry->buf_count++; - byte_count += PAGE_SIZE << page_order; - } - - DRM_DEBUG("byte_count: %d\n", byte_count); - - temp_buflist = krealloc(dma->buflist, - (dma->buf_count + entry->buf_count) * - sizeof(*dma->buflist), GFP_KERNEL); - if (!temp_buflist) { - /* Free the entry because it isn't valid */ - drm_cleanup_buf_error(dev, entry); - mutex_unlock(&dev->struct_mutex); - atomic_dec(&dev->buf_alloc); - return -ENOMEM; - } - dma->buflist = temp_buflist; - - for (i = 0; i < entry->buf_count; i++) { - dma->buflist[i + dma->buf_count] = &entry->buflist[i]; - } - - dma->buf_count += entry->buf_count; - dma->seg_count += entry->seg_count; - dma->page_count += byte_count >> PAGE_SHIFT; - dma->byte_count += byte_count; - - DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); - DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); - - mutex_unlock(&dev->struct_mutex); - - request->count = entry->buf_count; - request->size = size; - - dma->flags = _DRM_DMA_USE_FB; - - atomic_dec(&dev->buf_alloc); - return 0; -} - - /** * Add buffers for DMA transfers (ioctl). * @@ -1319,7 +1164,7 @@ int drm_addbufs(struct drm_device *dev, void *data, if (request->flags & _DRM_SG_BUFFER) ret = drm_addbufs_sg(dev, request); else if (request->flags & _DRM_FB_BUFFER) - ret = drm_addbufs_fb(dev, request); + ret = -EINVAL; else ret = drm_addbufs_pci(dev, request); @@ -1556,9 +1401,7 @@ int drm_mapbufs(struct drm_device *dev, void *data, if (request->count >= dma->buf_count) { if ((drm_core_has_AGP(dev) && (dma->flags & _DRM_DMA_USE_AGP)) || (drm_core_check_feature(dev, DRIVER_SG) - && (dma->flags & _DRM_DMA_USE_SG)) - || (drm_core_check_feature(dev, DRIVER_FB_DMA) - && (dma->flags & _DRM_DMA_USE_FB))) { + && (dma->flags & _DRM_DMA_USE_SG))) { struct drm_local_map *map = dev->agp_buffer_map; unsigned long token = dev->agp_buffer_token; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index cef9a507246e..922e426b3391 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -147,7 +147,6 @@ int drm_err(const char *func, const char *format, ...); #define DRIVER_IRQ_SHARED 0x80 #define DRIVER_IRQ_VBL 0x100 #define DRIVER_DMA_QUEUE 0x200 -#define DRIVER_FB_DMA 0x400 #define DRIVER_IRQ_VBL2 0x800 #define DRIVER_GEM 0x1000 #define DRIVER_MODESET 0x2000 -- cgit v1.2.3-71-gd317 From 74867e3d53e41afe2a093196850167542fa508d2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Aug 2013 15:41:25 +0200 Subject: drm: rip out a few unused DRIVER flags The gma500 driver somehow set the DRIVER_IRQ_VBL flag, but since there's no code at all to check for this we can kill it. The other two are completely unused. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/gma500/psb_drv.c | 2 +- include/drm/drmP.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 99b5293972c6..1383e75acf25 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -627,7 +627,7 @@ static const struct file_operations psb_gem_fops = { static struct drm_driver driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | \ - DRIVER_IRQ_VBL | DRIVER_MODESET | DRIVER_GEM , + DRIVER_MODESET | DRIVER_GEM , .load = psb_driver_load, .unload = psb_driver_unload, diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 922e426b3391..0dc64fca3ebb 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -145,9 +145,6 @@ int drm_err(const char *func, const char *format, ...); #define DRIVER_HAVE_DMA 0x20 #define DRIVER_HAVE_IRQ 0x40 #define DRIVER_IRQ_SHARED 0x80 -#define DRIVER_IRQ_VBL 0x100 -#define DRIVER_DMA_QUEUE 0x200 -#define DRIVER_IRQ_VBL2 0x800 #define DRIVER_GEM 0x1000 #define DRIVER_MODESET 0x2000 #define DRIVER_PRIME 0x4000 -- cgit v1.2.3-71-gd317 From fac3eaffb1139156099b6111337525d47e65bca6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Aug 2013 15:41:26 +0200 Subject: drm: remove a bunch of unused #defines from drmP.h Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- include/drm/drmP.h | 9 --------- 1 file changed, 9 deletions(-) (limited to 'include') diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 0dc64fca3ebb..6a0918e7db65 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -163,13 +163,7 @@ int drm_err(const char *func, const char *format, ...); #define DRM_MAGIC_HASH_ORDER 4 /**< Size of key hash table. Must be power of 2. */ #define DRM_KERNEL_CONTEXT 0 /**< Change drm_resctx if changed */ #define DRM_RESERVED_CONTEXTS 1 /**< Change drm_resctx if changed */ -#define DRM_LOOPING_LIMIT 5000000 -#define DRM_TIME_SLICE (HZ/20) /**< Time slice for GLXContexts */ -#define DRM_LOCK_SLICE 1 /**< Time slice for lock, in jiffies */ -#define DRM_FLAG_DEBUG 0x01 - -#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) #define DRM_MAP_HASH_OFFSET 0x10000000 /*@}*/ @@ -258,9 +252,6 @@ int drm_err(const char *func, const char *format, ...); #define DRM_ARRAY_SIZE(x) ARRAY_SIZE(x) -#define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1)) -#define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x)) - #define DRM_IF_VERSION(maj, min) (maj << 16 | min) /** -- cgit v1.2.3-71-gd317 From b17df86ece5e674c6f50b9a219067b9a80ea9dd1 Mon Sep 17 00:00:00 2001 From: Stéphane Marchesin Date: Tue, 13 Aug 2013 11:55:12 -0700 Subject: drm: Remove drm_mode_validate_clocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is unused. Signed-off-by: Stéphane Marchesin Reviewed-by: David Herrmann Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_modes.c | 37 ------------------------------------- include/drm/drm_crtc.h | 3 --- 2 files changed, 40 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index a6729bfe6860..504a602f495c 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -922,43 +922,6 @@ void drm_mode_validate_size(struct drm_device *dev, } EXPORT_SYMBOL(drm_mode_validate_size); -/** - * drm_mode_validate_clocks - validate modes against clock limits - * @dev: DRM device - * @mode_list: list of modes to check - * @min: minimum clock rate array - * @max: maximum clock rate array - * @n_ranges: number of clock ranges (size of arrays) - * - * LOCKING: - * Caller must hold a lock protecting @mode_list. - * - * Some code may need to check a mode list against the clock limits of the - * device in question. This function walks the mode list, testing to make - * sure each mode falls within a given range (defined by @min and @max - * arrays) and sets @mode->status as needed. - */ -void drm_mode_validate_clocks(struct drm_device *dev, - struct list_head *mode_list, - int *min, int *max, int n_ranges) -{ - struct drm_display_mode *mode; - int i; - - list_for_each_entry(mode, mode_list, head) { - bool good = false; - for (i = 0; i < n_ranges; i++) { - if (mode->clock >= min[i] && mode->clock <= max[i]) { - good = true; - break; - } - } - if (!good) - mode->status = MODE_CLOCK_RANGE; - } -} -EXPORT_SYMBOL(drm_mode_validate_clocks); - /** * drm_mode_prune_invalid - remove invalid modes from mode list * @dev: DRM device diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index fa12a2fa4293..32e0820357e6 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -930,9 +930,6 @@ extern void drm_mode_list_concat(struct list_head *head, extern void drm_mode_validate_size(struct drm_device *dev, struct list_head *mode_list, int maxX, int maxY, int maxPitch); -extern void drm_mode_validate_clocks(struct drm_device *dev, - struct list_head *mode_list, - int *min, int *max, int n_ranges); extern void drm_mode_prune_invalid(struct drm_device *dev, struct list_head *mode_list, bool verbose); extern void drm_mode_sort(struct list_head *mode_list); -- cgit v1.2.3-71-gd317 From cabaafc78935521c5abc7ec72278dbaa5400c995 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 7 Aug 2013 14:41:54 -0400 Subject: drm: add flip-work helper A small helper to queue up work to do, from workqueue context, after a flip. Typically useful to defer unreffing buffers that may be read by the display controller until vblank. v1: original v2: wire up docbook + couple docbook fixes Signed-off-by: Rob Clark Acked-by: Daniel Vetter Signed-off-by: Dave Airlie --- Documentation/DocBook/drm.tmpl | 6 ++ drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_flip_work.c | 124 ++++++++++++++++++++++++++++++++++++++++ include/drm/drm_flip_work.h | 76 ++++++++++++++++++++++++ 4 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_flip_work.c create mode 100644 include/drm/drm_flip_work.h (limited to 'include') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 9494ab8af9e7..f97d08ba59be 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2211,6 +2211,12 @@ void intel_crt_init(struct drm_device *dev) !Pinclude/drm/drm_rect.h rect utils !Iinclude/drm/drm_rect.h !Edrivers/gpu/drm/drm_rect.c + + + Flip-work Helper Reference +!Pinclude/drm/drm_flip_work.h flip utils +!Iinclude/drm/drm_flip_work.h +!Edrivers/gpu/drm/drm_flip_work.c VMA Offset Manager diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index d943b94afc90..2aaf082368a2 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -13,7 +13,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_crtc.o drm_modes.o drm_edid.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ drm_trace_points.o drm_global.o drm_prime.o \ - drm_rect.o drm_vma_manager.o + drm_rect.o drm_vma_manager.o drm_flip_work.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o diff --git a/drivers/gpu/drm/drm_flip_work.c b/drivers/gpu/drm/drm_flip_work.c new file mode 100644 index 000000000000..e788882d9021 --- /dev/null +++ b/drivers/gpu/drm/drm_flip_work.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2013 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "drmP.h" +#include "drm_flip_work.h" + +/** + * drm_flip_work_queue - queue work + * @work: the flip-work + * @val: the value to queue + * + * Queues work, that will later be run (passed back to drm_flip_func_t + * func) on a work queue after drm_flip_work_commit() is called. + */ +void drm_flip_work_queue(struct drm_flip_work *work, void *val) +{ + if (kfifo_put(&work->fifo, (const void **)&val)) { + atomic_inc(&work->pending); + } else { + DRM_ERROR("%s fifo full!\n", work->name); + work->func(work, val); + } +} +EXPORT_SYMBOL(drm_flip_work_queue); + +/** + * drm_flip_work_commit - commit queued work + * @work: the flip-work + * @wq: the work-queue to run the queued work on + * + * Trigger work previously queued by drm_flip_work_queue() to run + * on a workqueue. The typical usage would be to queue work (via + * drm_flip_work_queue()) at any point (from vblank irq and/or + * prior), and then from vblank irq commit the queued work. + */ +void drm_flip_work_commit(struct drm_flip_work *work, + struct workqueue_struct *wq) +{ + uint32_t pending = atomic_read(&work->pending); + atomic_add(pending, &work->count); + atomic_sub(pending, &work->pending); + queue_work(wq, &work->worker); +} +EXPORT_SYMBOL(drm_flip_work_commit); + +static void flip_worker(struct work_struct *w) +{ + struct drm_flip_work *work = container_of(w, struct drm_flip_work, worker); + uint32_t count = atomic_read(&work->count); + void *val = NULL; + + atomic_sub(count, &work->count); + + while(count--) + if (!WARN_ON(!kfifo_get(&work->fifo, &val))) + work->func(work, val); +} + +/** + * drm_flip_work_init - initialize flip-work + * @work: the flip-work to initialize + * @size: the max queue depth + * @name: debug name + * @func: the callback work function + * + * Initializes/allocates resources for the flip-work + * + * RETURNS: + * Zero on success, error code on failure. + */ +int drm_flip_work_init(struct drm_flip_work *work, int size, + const char *name, drm_flip_func_t func) +{ + int ret; + + work->name = name; + atomic_set(&work->count, 0); + atomic_set(&work->pending, 0); + work->func = func; + + ret = kfifo_alloc(&work->fifo, size, GFP_KERNEL); + if (ret) { + DRM_ERROR("could not allocate %s fifo\n", name); + return ret; + } + + INIT_WORK(&work->worker, flip_worker); + + return 0; +} +EXPORT_SYMBOL(drm_flip_work_init); + +/** + * drm_flip_work_cleanup - cleans up flip-work + * @work: the flip-work to cleanup + * + * Destroy resources allocated for the flip-work + */ +void drm_flip_work_cleanup(struct drm_flip_work *work) +{ + WARN_ON(!kfifo_is_empty(&work->fifo)); + kfifo_free(&work->fifo); +} +EXPORT_SYMBOL(drm_flip_work_cleanup); diff --git a/include/drm/drm_flip_work.h b/include/drm/drm_flip_work.h new file mode 100644 index 000000000000..35c776ae7d3b --- /dev/null +++ b/include/drm/drm_flip_work.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2013 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef DRM_FLIP_WORK_H +#define DRM_FLIP_WORK_H + +#include +#include + +/** + * DOC: flip utils + * + * Util to queue up work to run from work-queue context after flip/vblank. + * Typically this can be used to defer unref of framebuffer's, cursor + * bo's, etc until after vblank. The APIs are all safe (and lockless) + * for up to one producer and once consumer at a time. The single-consumer + * aspect is ensured by committing the queued work to a single work-queue. + */ + +struct drm_flip_work; + +/* + * drm_flip_func_t - callback function + * + * @work: the flip work + * @val: value queued via drm_flip_work_queue() + * + * Callback function to be called for each of the queue'd work items after + * drm_flip_work_commit() is called. + */ +typedef void (*drm_flip_func_t)(struct drm_flip_work *work, void *val); + +/** + * struct drm_flip_work - flip work queue + * @name: debug name + * @pending: number of queued but not committed items + * @count: number of committed items + * @func: callback fxn called for each committed item + * @worker: worker which calls @func + */ +struct drm_flip_work { + const char *name; + atomic_t pending, count; + drm_flip_func_t func; + struct work_struct worker; + DECLARE_KFIFO_PTR(fifo, void *); +}; + +void drm_flip_work_queue(struct drm_flip_work *work, void *val); +void drm_flip_work_commit(struct drm_flip_work *work, + struct workqueue_struct *wq); +int drm_flip_work_init(struct drm_flip_work *work, int size, + const char *name, drm_flip_func_t func); +void drm_flip_work_cleanup(struct drm_flip_work *work); + +#endif /* DRM_FLIP_WORK_H */ -- cgit v1.2.3-71-gd317 From 367bbd49202dd256dce1217c2f7cd0d5d1916f7b Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 7 Aug 2013 13:41:23 -0400 Subject: drm/gem: add drm_gem_create_mmap_offset_size() Variant of drm_gem_create_mmap_offset() which doesn't make the assumption that virtual size and physical size (obj->size) are the same. This is needed in omapdrm to deal with tiled buffers. And lets us get rid of a duplicated and slightly modified version of drm_gem_create_mmap_offset() in omapdrm. Signed-off-by: Rob Clark Reviewed-by: David Herrmann Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 28 ++++++++++++++++++++++++---- include/drm/drmP.h | 1 + 2 files changed, 25 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 9ab038c8dd5f..a8ba7da83d45 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -317,24 +317,44 @@ drm_gem_free_mmap_offset(struct drm_gem_object *obj) EXPORT_SYMBOL(drm_gem_free_mmap_offset); /** - * drm_gem_create_mmap_offset - create a fake mmap offset for an object + * drm_gem_create_mmap_offset_size - create a fake mmap offset for an object * @obj: obj in question + * @size: the virtual size * * GEM memory mapping works by handing back to userspace a fake mmap offset * it can use in a subsequent mmap(2) call. The DRM core code then looks * up the object based on the offset and sets up the various memory mapping * structures. * - * This routine allocates and attaches a fake offset for @obj. + * This routine allocates and attaches a fake offset for @obj, in cases where + * the virtual size differs from the physical size (ie. obj->size). Otherwise + * just use drm_gem_create_mmap_offset(). */ int -drm_gem_create_mmap_offset(struct drm_gem_object *obj) +drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size) { struct drm_device *dev = obj->dev; struct drm_gem_mm *mm = dev->mm_private; return drm_vma_offset_add(&mm->vma_manager, &obj->vma_node, - obj->size / PAGE_SIZE); + size / PAGE_SIZE); +} +EXPORT_SYMBOL(drm_gem_create_mmap_offset_size); + +/** + * drm_gem_create_mmap_offset - create a fake mmap offset for an object + * @obj: obj in question + * + * GEM memory mapping works by handing back to userspace a fake mmap offset + * it can use in a subsequent mmap(2) call. The DRM core code then looks + * up the object based on the offset and sets up the various memory mapping + * structures. + * + * This routine allocates and attaches a fake offset for @obj. + */ +int drm_gem_create_mmap_offset(struct drm_gem_object *obj) +{ + return drm_gem_create_mmap_offset_size(obj, obj->size); } EXPORT_SYMBOL(drm_gem_create_mmap_offset); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 6a0918e7db65..d6d9a28fc6b4 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1614,6 +1614,7 @@ drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) void drm_gem_free_mmap_offset(struct drm_gem_object *obj); int drm_gem_create_mmap_offset(struct drm_gem_object *obj); +int drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size); struct drm_gem_object *drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp, -- cgit v1.2.3-71-gd317 From bcc5c9d50e93bb7d949f6f38063b62dd35ca84d1 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 7 Aug 2013 13:41:24 -0400 Subject: drm/gem: add shmem get/put page helpers Basically just extracting some code duplicated in gma500, omapdrm, udl, and upcoming msm driver. Signed-off-by: Rob Clark Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++ include/drm/drmP.h | 4 ++ 2 files changed, 107 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index a8ba7da83d45..a4c8e8fba599 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -358,6 +358,109 @@ int drm_gem_create_mmap_offset(struct drm_gem_object *obj) } EXPORT_SYMBOL(drm_gem_create_mmap_offset); +/** + * drm_gem_get_pages - helper to allocate backing pages for a GEM object + * from shmem + * @obj: obj in question + * @gfpmask: gfp mask of requested pages + */ +struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) +{ + struct inode *inode; + struct address_space *mapping; + struct page *p, **pages; + int i, npages; + + /* This is the shared memory object that backs the GEM resource */ + inode = file_inode(obj->filp); + mapping = inode->i_mapping; + + /* We already BUG_ON() for non-page-aligned sizes in + * drm_gem_object_init(), so we should never hit this unless + * driver author is doing something really wrong: + */ + WARN_ON((obj->size & (PAGE_SIZE - 1)) != 0); + + npages = obj->size >> PAGE_SHIFT; + + pages = drm_malloc_ab(npages, sizeof(struct page *)); + if (pages == NULL) + return ERR_PTR(-ENOMEM); + + gfpmask |= mapping_gfp_mask(mapping); + + for (i = 0; i < npages; i++) { + p = shmem_read_mapping_page_gfp(mapping, i, gfpmask); + if (IS_ERR(p)) + goto fail; + pages[i] = p; + + /* There is a hypothetical issue w/ drivers that require + * buffer memory in the low 4GB.. if the pages are un- + * pinned, and swapped out, they can end up swapped back + * in above 4GB. If pages are already in memory, then + * shmem_read_mapping_page_gfp will ignore the gfpmask, + * even if the already in-memory page disobeys the mask. + * + * It is only a theoretical issue today, because none of + * the devices with this limitation can be populated with + * enough memory to trigger the issue. But this BUG_ON() + * is here as a reminder in case the problem with + * shmem_read_mapping_page_gfp() isn't solved by the time + * it does become a real issue. + * + * See this thread: http://lkml.org/lkml/2011/7/11/238 + */ + BUG_ON((gfpmask & __GFP_DMA32) && + (page_to_pfn(p) >= 0x00100000UL)); + } + + return pages; + +fail: + while (i--) + page_cache_release(pages[i]); + + drm_free_large(pages); + return ERR_CAST(p); +} +EXPORT_SYMBOL(drm_gem_get_pages); + +/** + * drm_gem_put_pages - helper to free backing pages for a GEM object + * @obj: obj in question + * @pages: pages to free + * @dirty: if true, pages will be marked as dirty + * @accessed: if true, the pages will be marked as accessed + */ +void drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages, + bool dirty, bool accessed) +{ + int i, npages; + + /* We already BUG_ON() for non-page-aligned sizes in + * drm_gem_object_init(), so we should never hit this unless + * driver author is doing something really wrong: + */ + WARN_ON((obj->size & (PAGE_SIZE - 1)) != 0); + + npages = obj->size >> PAGE_SHIFT; + + for (i = 0; i < npages; i++) { + if (dirty) + set_page_dirty(pages[i]); + + if (accessed) + mark_page_accessed(pages[i]); + + /* Undo the reference we took when populating the table */ + page_cache_release(pages[i]); + } + + drm_free_large(pages); +} +EXPORT_SYMBOL(drm_gem_put_pages); + /** Returns a reference to the object named by the handle. */ struct drm_gem_object * drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp, diff --git a/include/drm/drmP.h b/include/drm/drmP.h index d6d9a28fc6b4..91f343c8b160 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1616,6 +1616,10 @@ void drm_gem_free_mmap_offset(struct drm_gem_object *obj); int drm_gem_create_mmap_offset(struct drm_gem_object *obj); int drm_gem_create_mmap_offset_size(struct drm_gem_object *obj, size_t size); +struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask); +void drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages, + bool dirty, bool accessed); + struct drm_gem_object *drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp, u32 handle); -- cgit v1.2.3-71-gd317 From b778ae25366e6f3891fe51306f56a3bca211975d Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 23 Apr 2013 12:51:11 -0700 Subject: debugobjects: Make debug_object_activate() return status In order to better respond to things like duplicate invocations of call_rcu(), RCU needs to see the status of a call to debug_object_activate(). This would allow RCU to leak the callback in order to avoid adding freelist-reuse mischief to the duplicate invoations. This commit therefore makes debug_object_activate() return status, zero for success and -EINVAL for failure. Signed-off-by: Paul E. McKenney Cc: Mathieu Desnoyers Cc: Sedat Dilek Cc: Davidlohr Bueso Cc: Rik van Riel Cc: Thomas Gleixner Cc: Linus Torvalds Tested-by: Sedat Dilek Reviewed-by: Josh Triplett --- include/linux/debugobjects.h | 6 +++--- lib/debugobjects.c | 20 ++++++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/debugobjects.h b/include/linux/debugobjects.h index 0e5f5785d9f2..98ffcbd4888e 100644 --- a/include/linux/debugobjects.h +++ b/include/linux/debugobjects.h @@ -63,7 +63,7 @@ struct debug_obj_descr { extern void debug_object_init (void *addr, struct debug_obj_descr *descr); extern void debug_object_init_on_stack(void *addr, struct debug_obj_descr *descr); -extern void debug_object_activate (void *addr, struct debug_obj_descr *descr); +extern int debug_object_activate (void *addr, struct debug_obj_descr *descr); extern void debug_object_deactivate(void *addr, struct debug_obj_descr *descr); extern void debug_object_destroy (void *addr, struct debug_obj_descr *descr); extern void debug_object_free (void *addr, struct debug_obj_descr *descr); @@ -85,8 +85,8 @@ static inline void debug_object_init (void *addr, struct debug_obj_descr *descr) { } static inline void debug_object_init_on_stack(void *addr, struct debug_obj_descr *descr) { } -static inline void -debug_object_activate (void *addr, struct debug_obj_descr *descr) { } +static inline int +debug_object_activate (void *addr, struct debug_obj_descr *descr) { return 0; } static inline void debug_object_deactivate(void *addr, struct debug_obj_descr *descr) { } static inline void diff --git a/lib/debugobjects.c b/lib/debugobjects.c index 37061ede8b81..bf2c8b1043d8 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -381,19 +381,21 @@ void debug_object_init_on_stack(void *addr, struct debug_obj_descr *descr) * debug_object_activate - debug checks when an object is activated * @addr: address of the object * @descr: pointer to an object specific debug description structure + * Returns 0 for success, -EINVAL for check failed. */ -void debug_object_activate(void *addr, struct debug_obj_descr *descr) +int debug_object_activate(void *addr, struct debug_obj_descr *descr) { enum debug_obj_state state; struct debug_bucket *db; struct debug_obj *obj; unsigned long flags; + int ret; struct debug_obj o = { .object = addr, .state = ODEBUG_STATE_NOTAVAILABLE, .descr = descr }; if (!debug_objects_enabled) - return; + return 0; db = get_bucket((unsigned long) addr); @@ -405,23 +407,26 @@ void debug_object_activate(void *addr, struct debug_obj_descr *descr) case ODEBUG_STATE_INIT: case ODEBUG_STATE_INACTIVE: obj->state = ODEBUG_STATE_ACTIVE; + ret = 0; break; case ODEBUG_STATE_ACTIVE: debug_print_object(obj, "activate"); state = obj->state; raw_spin_unlock_irqrestore(&db->lock, flags); - debug_object_fixup(descr->fixup_activate, addr, state); - return; + ret = debug_object_fixup(descr->fixup_activate, addr, state); + return ret ? -EINVAL : 0; case ODEBUG_STATE_DESTROYED: debug_print_object(obj, "activate"); + ret = -EINVAL; break; default: + ret = 0; break; } raw_spin_unlock_irqrestore(&db->lock, flags); - return; + return ret; } raw_spin_unlock_irqrestore(&db->lock, flags); @@ -431,8 +436,11 @@ void debug_object_activate(void *addr, struct debug_obj_descr *descr) * true or not. */ if (debug_object_fixup(descr->fixup_activate, addr, - ODEBUG_STATE_NOTAVAILABLE)) + ODEBUG_STATE_NOTAVAILABLE)) { debug_print_object(&o, "activate"); + return -EINVAL; + } + return 0; } /** -- cgit v1.2.3-71-gd317 From c34ac00caefbe49d40058ae7200bd58725cebb45 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 28 Jun 2013 10:34:48 -0700 Subject: rculist: list_first_or_null_rcu() should use list_entry_rcu() list_first_or_null() should test whether the list is empty and return pointer to the first entry if not in a RCU safe manner. It's broken in several ways. * It compares __kernel @__ptr with __rcu @__next triggering the following sparse warning. net/core/dev.c:4331:17: error: incompatible types in comparison expression (different address spaces) * It doesn't perform rcu_dereference*() and computes the entry address using container_of() directly from the __rcu pointer which is inconsitent with other rculist interface. As a result, all three in-kernel users - net/core/dev.c, macvlan, cgroup - are buggy. They dereference the pointer w/o going through read barrier. * While ->next dereference passes through list_next_rcu(), the compiler is still free to fetch ->next more than once and thus nullify the "__ptr != __next" condition check. Fix it by making list_first_or_null_rcu() dereference ->next directly using ACCESS_ONCE() and then use list_entry_rcu() on it like other rculist accessors. v2: Paul pointed out that the compiler may fetch the pointer more than once nullifying the condition check. ACCESS_ONCE() added on ->next dereference. v3: Restored () around macro param which was accidentally removed. Spotted by Paul. Signed-off-by: Tejun Heo Reported-by: Fengguang Wu Cc: Dipankar Sarma Cc: "Paul E. McKenney" Cc: "David S. Miller" Cc: Li Zefan Cc: Patrick McHardy Cc: stable@vger.kernel.org Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/linux/rculist.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/rculist.h b/include/linux/rculist.h index f4b1001a4676..4106721c4e5e 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -267,8 +267,9 @@ static inline void list_splice_init_rcu(struct list_head *list, */ #define list_first_or_null_rcu(ptr, type, member) \ ({struct list_head *__ptr = (ptr); \ - struct list_head __rcu *__next = list_next_rcu(__ptr); \ - likely(__ptr != __next) ? container_of(__next, type, member) : NULL; \ + struct list_head *__next = ACCESS_ONCE(__ptr->next); \ + likely(__ptr != __next) ? \ + list_entry_rcu(__next, type, member) : NULL; \ }) /** -- cgit v1.2.3-71-gd317 From b21e3afe2357c0f49348a5fb61247012bf8262ec Mon Sep 17 00:00:00 2001 From: Ilia Mirkin Date: Wed, 7 Aug 2013 22:34:48 -0400 Subject: drm: use ida to allocate connector ids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes it so that reloading a module does not cause all the connector ids to change, which are user-visible and sometimes used for configuration. Signed-off-by: Ilia Mirkin Reviewed-by: Ville Syrjälä Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 62 ++++++++++++++++++++++++++++++++-------------- drivers/gpu/drm/drm_drv.c | 2 ++ include/drm/drm_crtc.h | 2 ++ 3 files changed, 48 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index fc83bb9eb514..a6917645fb4a 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -186,29 +186,29 @@ static const struct drm_prop_enum_list drm_dirty_info_enum_list[] = { struct drm_conn_prop_enum_list { int type; const char *name; - int count; + struct ida ida; }; /* * Connector and encoder types. */ static struct drm_conn_prop_enum_list drm_connector_enum_list[] = -{ { DRM_MODE_CONNECTOR_Unknown, "Unknown", 0 }, - { DRM_MODE_CONNECTOR_VGA, "VGA", 0 }, - { DRM_MODE_CONNECTOR_DVII, "DVI-I", 0 }, - { DRM_MODE_CONNECTOR_DVID, "DVI-D", 0 }, - { DRM_MODE_CONNECTOR_DVIA, "DVI-A", 0 }, - { DRM_MODE_CONNECTOR_Composite, "Composite", 0 }, - { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO", 0 }, - { DRM_MODE_CONNECTOR_LVDS, "LVDS", 0 }, - { DRM_MODE_CONNECTOR_Component, "Component", 0 }, - { DRM_MODE_CONNECTOR_9PinDIN, "DIN", 0 }, - { DRM_MODE_CONNECTOR_DisplayPort, "DP", 0 }, - { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A", 0 }, - { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B", 0 }, - { DRM_MODE_CONNECTOR_TV, "TV", 0 }, - { DRM_MODE_CONNECTOR_eDP, "eDP", 0 }, - { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual", 0}, +{ { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, + { DRM_MODE_CONNECTOR_VGA, "VGA" }, + { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, + { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, + { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, + { DRM_MODE_CONNECTOR_Composite, "Composite" }, + { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" }, + { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, + { DRM_MODE_CONNECTOR_Component, "Component" }, + { DRM_MODE_CONNECTOR_9PinDIN, "DIN" }, + { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, + { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, + { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, + { DRM_MODE_CONNECTOR_TV, "TV" }, + { DRM_MODE_CONNECTOR_eDP, "eDP" }, + { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, }; static const struct drm_prop_enum_list drm_encoder_enum_list[] = @@ -220,6 +220,22 @@ static const struct drm_prop_enum_list drm_encoder_enum_list[] = { DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, }; +void drm_connector_ida_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) + ida_init(&drm_connector_enum_list[i].ida); +} + +void drm_connector_ida_destroy(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) + ida_destroy(&drm_connector_enum_list[i].ida); +} + const char *drm_get_encoder_name(const struct drm_encoder *encoder) { static char buf[32]; @@ -711,6 +727,8 @@ int drm_connector_init(struct drm_device *dev, int connector_type) { int ret; + struct ida *connector_ida = + &drm_connector_enum_list[connector_type].ida; drm_modeset_lock_all(dev); @@ -723,7 +741,12 @@ int drm_connector_init(struct drm_device *dev, connector->funcs = funcs; connector->connector_type = connector_type; connector->connector_type_id = - ++drm_connector_enum_list[connector_type].count; /* TODO */ + ida_simple_get(connector_ida, 1, 0, GFP_KERNEL); + if (connector->connector_type_id < 0) { + ret = connector->connector_type_id; + drm_mode_object_put(dev, &connector->base); + goto out; + } INIT_LIST_HEAD(&connector->probed_modes); INIT_LIST_HEAD(&connector->modes); connector->edid_blob_ptr = NULL; @@ -764,6 +787,9 @@ void drm_connector_cleanup(struct drm_connector *connector) list_for_each_entry_safe(mode, t, &connector->modes, head) drm_mode_remove(connector, mode); + ida_remove(&drm_connector_enum_list[connector->connector_type].ida, + connector->connector_type_id); + drm_mode_object_put(dev, &connector->base); list_del(&connector->head); dev->mode_config.num_connector--; diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 5b949a736712..d97976cc51cd 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -226,6 +226,7 @@ static int __init drm_core_init(void) int ret = -ENOMEM; drm_global_init(); + drm_connector_ida_init(); idr_init(&drm_minors_idr); if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops)) @@ -273,6 +274,7 @@ static void __exit drm_core_exit(void) unregister_chrdev(DRM_MAJOR, "drm"); + drm_connector_ida_destroy(); idr_destroy(&drm_minors_idr); } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 32e0820357e6..45f133228553 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -869,6 +869,8 @@ extern int drm_crtc_init(struct drm_device *dev, const struct drm_crtc_funcs *funcs); extern void drm_crtc_cleanup(struct drm_crtc *crtc); +extern void drm_connector_ida_init(void); +extern void drm_connector_ida_destroy(void); extern int drm_connector_init(struct drm_device *dev, struct drm_connector *connector, const struct drm_connector_funcs *funcs, -- cgit v1.2.3-71-gd317 From c1d6798d20eed38b842eee01813ca6c48630d563 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:30 +0200 Subject: drm: use common drm_gem_dmabuf_release in i915/exynos drivers Note that this is slightly tricky since both drivers store their native objects in dma_buf->priv. But both also embed the base drm_gem_object at the first position, so the implicit cast is ok. To use the release helper we need to export it, too. Cc: Inki Dae Cc: Intel Graphics Development Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_prime.c | 3 ++- drivers/gpu/drm/exynos/exynos_drm_dmabuf.c | 23 +---------------------- drivers/gpu/drm/i915/i915_gem_dmabuf.c | 13 +------------ include/drm/drmP.h | 1 + 4 files changed, 5 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 85e450e3241c..a35f206bdc34 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -192,7 +192,7 @@ static void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach, /* nothing to be done here */ } -static void drm_gem_dmabuf_release(struct dma_buf *dma_buf) +void drm_gem_dmabuf_release(struct dma_buf *dma_buf) { struct drm_gem_object *obj = dma_buf->priv; @@ -202,6 +202,7 @@ static void drm_gem_dmabuf_release(struct dma_buf *dma_buf) drm_gem_object_unreference_unlocked(obj); } } +EXPORT_SYMBOL(drm_gem_dmabuf_release); static void *drm_gem_dmabuf_vmap(struct dma_buf *dma_buf) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c index a0f997e0cbdf..3cd56e16a0ef 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c @@ -127,27 +127,6 @@ static void exynos_gem_unmap_dma_buf(struct dma_buf_attachment *attach, /* Nothing to do. */ } -static void exynos_dmabuf_release(struct dma_buf *dmabuf) -{ - struct exynos_drm_gem_obj *exynos_gem_obj = dmabuf->priv; - - /* - * exynos_dmabuf_release() call means that file object's - * f_count is 0 and it calls drm_gem_object_handle_unreference() - * to drop the references that these values had been increased - * at drm_prime_handle_to_fd() - */ - if (exynos_gem_obj->base.export_dma_buf == dmabuf) { - exynos_gem_obj->base.export_dma_buf = NULL; - - /* - * drop this gem object refcount to release allocated buffer - * and resources. - */ - drm_gem_object_unreference_unlocked(&exynos_gem_obj->base); - } -} - static void *exynos_gem_dmabuf_kmap_atomic(struct dma_buf *dma_buf, unsigned long page_num) { @@ -193,7 +172,7 @@ static struct dma_buf_ops exynos_dmabuf_ops = { .kunmap = exynos_gem_dmabuf_kunmap, .kunmap_atomic = exynos_gem_dmabuf_kunmap_atomic, .mmap = exynos_gem_dmabuf_mmap, - .release = exynos_dmabuf_release, + .release = drm_gem_dmabuf_release, }; struct dma_buf *exynos_dmabuf_prime_export(struct drm_device *drm_dev, diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index f2e185c9038f..63ee1a9f7978 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -90,17 +90,6 @@ static void i915_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, kfree(sg); } -static void i915_gem_dmabuf_release(struct dma_buf *dma_buf) -{ - struct drm_i915_gem_object *obj = dma_buf->priv; - - if (obj->base.export_dma_buf == dma_buf) { - /* drop the reference on the export fd holds */ - obj->base.export_dma_buf = NULL; - drm_gem_object_unreference_unlocked(&obj->base); - } -} - static void *i915_gem_dmabuf_vmap(struct dma_buf *dma_buf) { struct drm_i915_gem_object *obj = dma_buf->priv; @@ -211,7 +200,7 @@ static int i915_gem_begin_cpu_access(struct dma_buf *dma_buf, size_t start, size static const struct dma_buf_ops i915_dmabuf_ops = { .map_dma_buf = i915_gem_map_dma_buf, .unmap_dma_buf = i915_gem_unmap_dma_buf, - .release = i915_gem_dmabuf_release, + .release = drm_gem_dmabuf_release, .kmap = i915_gem_dmabuf_kmap, .kmap_atomic = i915_gem_dmabuf_kmap_atomic, .kunmap = i915_gem_dmabuf_kunmap, diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 91f343c8b160..5ebed3ea10ba 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1478,6 +1478,7 @@ extern struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf); extern int drm_gem_prime_fd_to_handle(struct drm_device *dev, struct drm_file *file_priv, int prime_fd, uint32_t *handle); +extern void drm_gem_dmabuf_release(struct dma_buf *dma_buf); extern int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -- cgit v1.2.3-71-gd317 From 7106bf96f81b0c207aaab4b46aa2acc5cab334d4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:33 +0200 Subject: drm/prime: add a bit of documentation about gem_obj->import_attach Lifetime rules seem to be solid around ->import_attach. So this patch just properly documents them. Note that pointing directly at the attachment might have issues for devices that have multiple struct device *dev parts constituting the logical gpu and so might need multiple attachment points. Similarly for drm devices which don't need a dma attachment at all (like udl). But fixing that up is material for different patches. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- include/drm/drmP.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 5ebed3ea10ba..5dc98947375c 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -665,7 +665,16 @@ struct drm_gem_object { /* dma buf exported from this GEM object */ struct dma_buf *export_dma_buf; - /* dma buf attachment backing this object */ + /** + * import_attach - dma buf attachment backing this object + * + * Any foreign dma_buf imported as a gem object has this set to the + * attachment point for the device. This is invariant over the lifetime + * of a gem object. + * + * The driver's ->gem_free_object callback is responsible for cleaning + * up the dma_buf attachment and references acquired at import time. + */ struct dma_buf_attachment *import_attach; }; -- cgit v1.2.3-71-gd317 From 36da5908a275d6319c17e758b5bde89b4f573959 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:34 +0200 Subject: drm/gem: move drm_gem_object_handle_unreference_unlocked into drm_gem.c We have three callers of this function now and it's neither performance critical nor really small. So an inline function feels like overkill and unecessarily separates the different parts of the code. Since all callers of drm_gem_object_handle_free are now in drm_gem.c we can make that static (and remove the unused EXPORT_SYMBOL). To avoid a forward declaration move it (and drm_gem_object_free_bug) up a bit. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 89 ++++++++++++++++++++++++++++------------------- include/drm/drmP.h | 21 +---------- 2 files changed, 55 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index a4c8e8fba599..f3c1bbcf807f 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -201,6 +201,60 @@ drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) } } +static void drm_gem_object_ref_bug(struct kref *list_kref) +{ + BUG(); +} + +/** + * Called after the last handle to the object has been closed + * + * Removes any name for the object. Note that this must be + * called before drm_gem_object_free or we'll be touching + * freed memory + */ +static void drm_gem_object_handle_free(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + + /* Remove any name for this object */ + spin_lock(&dev->object_name_lock); + if (obj->name) { + idr_remove(&dev->object_name_idr, obj->name); + obj->name = 0; + spin_unlock(&dev->object_name_lock); + /* + * The object name held a reference to this object, drop + * that now. + * + * This cannot be the last reference, since the handle holds one too. + */ + kref_put(&obj->refcount, drm_gem_object_ref_bug); + } else + spin_unlock(&dev->object_name_lock); + +} + +void +drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) +{ + if (obj == NULL) + return; + + if (atomic_read(&obj->handle_count) == 0) + return; + + /* + * Must bump handle count first as this may be the last + * ref, in which case the object would disappear before we + * checked for a name + */ + + if (atomic_dec_and_test(&obj->handle_count)) + drm_gem_object_handle_free(obj); + drm_gem_object_unreference_unlocked(obj); +} + /** * Removes the mapping from handle to filp for this object. */ @@ -656,41 +710,6 @@ drm_gem_object_free(struct kref *kref) } EXPORT_SYMBOL(drm_gem_object_free); -static void drm_gem_object_ref_bug(struct kref *list_kref) -{ - BUG(); -} - -/** - * Called after the last handle to the object has been closed - * - * Removes any name for the object. Note that this must be - * called before drm_gem_object_free or we'll be touching - * freed memory - */ -void drm_gem_object_handle_free(struct drm_gem_object *obj) -{ - struct drm_device *dev = obj->dev; - - /* Remove any name for this object */ - spin_lock(&dev->object_name_lock); - if (obj->name) { - idr_remove(&dev->object_name_idr, obj->name); - obj->name = 0; - spin_unlock(&dev->object_name_lock); - /* - * The object name held a reference to this object, drop - * that now. - * - * This cannot be the last reference, since the handle holds one too. - */ - kref_put(&obj->refcount, drm_gem_object_ref_bug); - } else - spin_unlock(&dev->object_name_lock); - -} -EXPORT_SYMBOL(drm_gem_object_handle_free); - void drm_gem_vm_open(struct vm_area_struct *vma) { struct drm_gem_object *obj = vma->vm_private_data; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 5dc98947375c..0ef8e5481e15 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1555,7 +1555,6 @@ int drm_gem_object_init(struct drm_device *dev, struct drm_gem_object *obj, size_t size); void drm_gem_private_object_init(struct drm_device *dev, struct drm_gem_object *obj, size_t size); -void drm_gem_object_handle_free(struct drm_gem_object *obj); void drm_gem_vm_open(struct vm_area_struct *vma); void drm_gem_vm_close(struct vm_area_struct *vma); int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, @@ -1602,25 +1601,7 @@ drm_gem_object_handle_reference(struct drm_gem_object *obj) atomic_inc(&obj->handle_count); } -static inline void -drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) -{ - if (obj == NULL) - return; - - if (atomic_read(&obj->handle_count) == 0) - return; - - /* - * Must bump handle count first as this may be the last - * ref, in which case the object would disappear before we - * checked for a name - */ - - if (atomic_dec_and_test(&obj->handle_count)) - drm_gem_object_handle_free(obj); - drm_gem_object_unreference_unlocked(obj); -} +void drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj); void drm_gem_free_mmap_offset(struct drm_gem_object *obj); int drm_gem_create_mmap_offset(struct drm_gem_object *obj); -- cgit v1.2.3-71-gd317 From feed66ed26a53e700ca02ce1744fed7d0c647292 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 9 May 2013 08:55:54 -0700 Subject: rcu: Eliminate unused APIs intended for adaptive ticks The rcu_user_enter_after_irq() and rcu_user_exit_after_irq() functions were intended for use by adaptive ticks, but changes in implementation have rendered them unnecessary. This commit therefore removes them. Reported-by: Frederic Weisbecker Signed-off-by: Paul E. McKenney Reviewed-by: Josh Triplett --- include/linux/rcupdate.h | 4 ---- kernel/rcutree.c | 43 ------------------------------------------- 2 files changed, 47 deletions(-) (limited to 'include') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 0c38abbe6e35..30bea9c25735 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -229,13 +229,9 @@ extern void rcu_irq_exit(void); #ifdef CONFIG_RCU_USER_QS extern void rcu_user_enter(void); extern void rcu_user_exit(void); -extern void rcu_user_enter_after_irq(void); -extern void rcu_user_exit_after_irq(void); #else static inline void rcu_user_enter(void) { } static inline void rcu_user_exit(void) { } -static inline void rcu_user_enter_after_irq(void) { } -static inline void rcu_user_exit_after_irq(void) { } static inline void rcu_user_hooks_switch(struct task_struct *prev, struct task_struct *next) { } #endif /* CONFIG_RCU_USER_QS */ diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 338f1d1c1c66..8807019138c6 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c @@ -444,27 +444,6 @@ void rcu_user_enter(void) { rcu_eqs_enter(1); } - -/** - * rcu_user_enter_after_irq - inform RCU that we are going to resume userspace - * after the current irq returns. - * - * This is similar to rcu_user_enter() but in the context of a non-nesting - * irq. After this call, RCU enters into idle mode when the interrupt - * returns. - */ -void rcu_user_enter_after_irq(void) -{ - unsigned long flags; - struct rcu_dynticks *rdtp; - - local_irq_save(flags); - rdtp = &__get_cpu_var(rcu_dynticks); - /* Ensure this irq is interrupting a non-idle RCU state. */ - WARN_ON_ONCE(!(rdtp->dynticks_nesting & DYNTICK_TASK_MASK)); - rdtp->dynticks_nesting = 1; - local_irq_restore(flags); -} #endif /* CONFIG_RCU_USER_QS */ /** @@ -581,28 +560,6 @@ void rcu_user_exit(void) { rcu_eqs_exit(1); } - -/** - * rcu_user_exit_after_irq - inform RCU that we won't resume to userspace - * idle mode after the current non-nesting irq returns. - * - * This is similar to rcu_user_exit() but in the context of an irq. - * This is called when the irq has interrupted a userspace RCU idle mode - * context. When the current non-nesting interrupt returns after this call, - * the CPU won't restore the RCU idle mode. - */ -void rcu_user_exit_after_irq(void) -{ - unsigned long flags; - struct rcu_dynticks *rdtp; - - local_irq_save(flags); - rdtp = &__get_cpu_var(rcu_dynticks); - /* Ensure we are interrupting an RCU idle mode. */ - WARN_ON_ONCE(rdtp->dynticks_nesting & DYNTICK_TASK_NEST_MASK); - rdtp->dynticks_nesting += DYNTICK_TASK_EXIT_IDLE; - local_irq_restore(flags); -} #endif /* CONFIG_RCU_USER_QS */ /** -- cgit v1.2.3-71-gd317 From 281856477cdaba70032af502ee7192fe7aa54f69 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Aug 2013 15:41:27 +0200 Subject: drm: rip out drm_core_has_MTRR checks The new arch_phys_wc_add/del functions do the right thing both with and without MTRR support in the kernel. So we can drop these additional checks. David Herrmann suggest to also kill the DRIVER_USE_MTRR flag since it's now unused, which spurred me to do a bit a better audit of the affected drivers. David helped a lot in that. Quoting our mail discussion: On Wed, Jul 10, 2013 at 5:41 PM, David Herrmann wrote: > On Wed, Jul 10, 2013 at 5:22 PM, Daniel Vetter wrote: >> On Wed, Jul 10, 2013 at 3:51 PM, David Herrmann wrote: >>>> -#if __OS_HAS_MTRR >>>> -static inline int drm_core_has_MTRR(struct drm_device *dev) >>>> -{ >>>> - return drm_core_check_feature(dev, DRIVER_USE_MTRR); >>>> -} >>>> -#else >>>> -#define drm_core_has_MTRR(dev) (0) >>>> -#endif >>>> - >>> >>> That was the last user of DRIVER_USE_MTRR (apart from drivers setting >>> it in .driver_features). Any reason to keep it around? >> >> Yeah, I guess we could rip things out. Which will also force me to >> properly audit drivers for the eventual behaviour change this could >> entail (in case there's an x86 driver which did not ask for an mtrr, >> but iirc there isn't). > > david@david-mb ~/dev/kernel/linux $ for i in drivers/gpu/drm/* ; do if > test -d "$i" ; then if ! grep -q USE_MTRR -r $i ; then echo $i ; fi ; > fi ; done > drivers/gpu/drm/exynos > drivers/gpu/drm/gma500 > drivers/gpu/drm/i2c > drivers/gpu/drm/nouveau > drivers/gpu/drm/omapdrm > drivers/gpu/drm/qxl > drivers/gpu/drm/rcar-du > drivers/gpu/drm/shmobile > drivers/gpu/drm/tilcdc > drivers/gpu/drm/ttm > drivers/gpu/drm/udl > drivers/gpu/drm/vmwgfx > david@david-mb ~/dev/kernel/linux $ > > So for x86 gma500,nouveau,qxl,udl,vmwgfx don't set DRIVER_USE_MTRR. > But I cannot tell whether they break if we call arch_phys_wc_add/del, > anyway. At least nouveau seemed to work here, but it doesn't use AGP > or drm_bufs, I guess. Cool, thanks a lot for stitching together the list of drivers to look at. So for real KMS drivers it's the drives responsibility to add an mtrr if it needs one. nouvea, radeon, mgag200, i915 and vmwgfx do that already. Somehow the savage driver also ends up doing that, I have no idea why. Note that gma500 as a pure KMS driver doesn't need MTRR setup since the platforms that it supports all support PAT. So no MTRRs needed to get wc iomappings. The mtrr support in the drm core is all for legacy mappings of garts, framebuffers and registers. All legacy drivers set the USE_MTRR flag, so we're good there. All in all I think we can really just ditch this /endquote v2: Also kill DRIVER_USE_MTRR as suggested by David Herrmann v3: Rebase on top of David Herrmann's agp setup/cleanup changes. Cc: David Herrmann Cc: Andy Lutomirski Signed-off-by: Daniel Vetter Acked-by: Andy Lutomirski Reviewed-by: David Herrmann Signed-off-by: Dave Airlie --- drivers/gpu/drm/ast/ast_drv.c | 2 +- drivers/gpu/drm/cirrus/cirrus_drv.c | 2 +- drivers/gpu/drm/drm_bufs.c | 13 +++++-------- drivers/gpu/drm/drm_pci.c | 14 ++++++-------- drivers/gpu/drm/drm_vm.c | 3 +-- drivers/gpu/drm/i810/i810_drv.c | 2 +- drivers/gpu/drm/i915/i915_drv.c | 2 +- drivers/gpu/drm/mga/mga_drv.c | 2 +- drivers/gpu/drm/mgag200/mgag200_drv.c | 2 +- drivers/gpu/drm/r128/r128_drv.c | 2 +- drivers/gpu/drm/radeon/radeon_drv.c | 4 ++-- drivers/gpu/drm/savage/savage_drv.c | 2 +- drivers/gpu/drm/sis/sis_drv.c | 2 +- drivers/gpu/drm/tdfx/tdfx_drv.c | 1 - drivers/gpu/drm/via/via_drv.c | 2 +- include/drm/drmP.h | 11 ----------- 16 files changed, 24 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 60f1ce3998c3..32e270dc714e 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -197,7 +197,7 @@ static const struct file_operations ast_fops = { }; static struct drm_driver driver = { - .driver_features = DRIVER_USE_MTRR | DRIVER_MODESET | DRIVER_GEM, + .driver_features = DRIVER_MODESET | DRIVER_GEM, .dev_priv_size = 0, .load = ast_driver_load, diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index dd9c908ab3fc..138364d91782 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -87,7 +87,7 @@ static const struct file_operations cirrus_driver_fops = { #endif }; static struct drm_driver driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_USE_MTRR, + .driver_features = DRIVER_MODESET | DRIVER_GEM, .load = cirrus_driver_load, .unload = cirrus_driver_unload, .fops = &cirrus_driver_fops, diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index 5f73f0af6125..f63133b0a9ab 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -207,12 +207,10 @@ static int drm_addmap_core(struct drm_device * dev, resource_size_t offset, return 0; } - if (drm_core_has_MTRR(dev)) { - if (map->type == _DRM_FRAME_BUFFER || - (map->flags & _DRM_WRITE_COMBINING)) { - map->mtrr = - arch_phys_wc_add(map->offset, map->size); - } + if (map->type == _DRM_FRAME_BUFFER || + (map->flags & _DRM_WRITE_COMBINING)) { + map->mtrr = + arch_phys_wc_add(map->offset, map->size); } if (map->type == _DRM_REGISTERS) { if (map->flags & _DRM_WRITE_COMBINING) @@ -464,8 +462,7 @@ int drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map) iounmap(map->handle); /* FALLTHROUGH */ case _DRM_FRAME_BUFFER: - if (drm_core_has_MTRR(dev)) - arch_phys_wc_del(map->mtrr); + arch_phys_wc_del(map->mtrr); break; case _DRM_SHM: vfree(map->handle); diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 0f54ad8a9ced..3fca2db1c40c 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -272,12 +272,11 @@ static int drm_pci_agp_init(struct drm_device *dev) DRM_ERROR("Cannot initialize the agpgart module.\n"); return -EINVAL; } - if (drm_core_has_MTRR(dev)) { - if (dev->agp) - dev->agp->agp_mtrr = arch_phys_wc_add( - dev->agp->agp_info.aper_base, - dev->agp->agp_info.aper_size * - 1024 * 1024); + if (dev->agp) { + dev->agp->agp_mtrr = arch_phys_wc_add( + dev->agp->agp_info.aper_base, + dev->agp->agp_info.aper_size * + 1024 * 1024); } } return 0; @@ -286,8 +285,7 @@ static int drm_pci_agp_init(struct drm_device *dev) static void drm_pci_agp_destroy(struct drm_device *dev) { if (drm_core_has_AGP(dev) && dev->agp) { - if (drm_core_has_MTRR(dev)) - arch_phys_wc_del(dev->agp->agp_mtrr); + arch_phys_wc_del(dev->agp->agp_mtrr); drm_agp_clear(dev); drm_agp_destroy(dev->agp); dev->agp = NULL; diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index feb20035b2c4..b5c5af7328df 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -251,8 +251,7 @@ static void drm_vm_shm_close(struct vm_area_struct *vma) switch (map->type) { case _DRM_REGISTERS: case _DRM_FRAME_BUFFER: - if (drm_core_has_MTRR(dev)) - arch_phys_wc_del(map->mtrr); + arch_phys_wc_del(map->mtrr); iounmap(map->handle); break; case _DRM_SHM: diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c index d85c05b4877d..d8180d22cedd 100644 --- a/drivers/gpu/drm/i810/i810_drv.c +++ b/drivers/gpu/drm/i810/i810_drv.c @@ -57,7 +57,7 @@ static const struct file_operations i810_driver_fops = { static struct drm_driver driver = { .driver_features = - DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | DRIVER_USE_MTRR | + DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | DRIVER_HAVE_DMA, .dev_priv_size = sizeof(drm_i810_buf_priv_t), .load = i810_driver_load, diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 9411a745adaf..eec47bd00353 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1006,7 +1006,7 @@ static struct drm_driver driver = { * deal with them for Intel hardware. */ .driver_features = - DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR |*/ + DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM | DRIVER_PRIME, .load = i915_driver_load, .unload = i915_driver_unload, diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c index fe71e1e44e48..6b1a87c8aac5 100644 --- a/drivers/gpu/drm/mga/mga_drv.c +++ b/drivers/gpu/drm/mga/mga_drv.c @@ -58,7 +58,7 @@ static const struct file_operations mga_driver_fops = { static struct drm_driver driver = { .driver_features = - DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | + DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, .dev_priv_size = sizeof(drm_mga_buf_priv_t), .load = mga_driver_load, diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index b570127ae3b2..fcce7b2f8011 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -88,7 +88,7 @@ static const struct file_operations mgag200_driver_fops = { }; static struct drm_driver driver = { - .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_USE_MTRR, + .driver_features = DRIVER_GEM | DRIVER_MODESET, .load = mgag200_driver_load, .unload = mgag200_driver_unload, .fops = &mgag200_driver_fops, diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c index c2338cbc56ad..5bd307cd8da1 100644 --- a/drivers/gpu/drm/r128/r128_drv.c +++ b/drivers/gpu/drm/r128/r128_drv.c @@ -56,7 +56,7 @@ static const struct file_operations r128_driver_fops = { static struct drm_driver driver = { .driver_features = - DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | + DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG | DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, .dev_priv_size = sizeof(drm_r128_buf_priv_t), .load = r128_driver_load, diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 3e52331124de..1f93dd503646 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -275,7 +275,7 @@ static const struct file_operations radeon_driver_old_fops = { static struct drm_driver driver_old = { .driver_features = - DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | + DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG | DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED, .dev_priv_size = sizeof(drm_radeon_buf_priv_t), .load = radeon_driver_load, @@ -382,7 +382,7 @@ static const struct file_operations radeon_driver_kms_fops = { static struct drm_driver kms_driver = { .driver_features = - DRIVER_USE_AGP | DRIVER_USE_MTRR | + DRIVER_USE_AGP | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM | DRIVER_PRIME, .dev_priv_size = 0, diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c index 9135c8bd6fbc..3c030216e888 100644 --- a/drivers/gpu/drm/savage/savage_drv.c +++ b/drivers/gpu/drm/savage/savage_drv.c @@ -50,7 +50,7 @@ static const struct file_operations savage_driver_fops = { static struct drm_driver driver = { .driver_features = - DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_DMA | DRIVER_PCI_DMA, + DRIVER_USE_AGP | DRIVER_HAVE_DMA | DRIVER_PCI_DMA, .dev_priv_size = sizeof(drm_savage_buf_priv_t), .load = savage_driver_load, .firstopen = savage_driver_firstopen, diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c index b88b2d302105..4383b74a3aa4 100644 --- a/drivers/gpu/drm/sis/sis_drv.c +++ b/drivers/gpu/drm/sis/sis_drv.c @@ -102,7 +102,7 @@ void sis_driver_postclose(struct drm_device *dev, struct drm_file *file) } static struct drm_driver driver = { - .driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR, + .driver_features = DRIVER_USE_AGP, .load = sis_driver_load, .unload = sis_driver_unload, .open = sis_driver_open, diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c index 951ec13e4e5c..3492ca5c46d3 100644 --- a/drivers/gpu/drm/tdfx/tdfx_drv.c +++ b/drivers/gpu/drm/tdfx/tdfx_drv.c @@ -55,7 +55,6 @@ static const struct file_operations tdfx_driver_fops = { }; static struct drm_driver driver = { - .driver_features = DRIVER_USE_MTRR, .fops = &tdfx_driver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c index 448799968a06..92684a9b7e34 100644 --- a/drivers/gpu/drm/via/via_drv.c +++ b/drivers/gpu/drm/via/via_drv.c @@ -72,7 +72,7 @@ static const struct file_operations via_driver_fops = { static struct drm_driver driver = { .driver_features = - DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_IRQ | + DRIVER_USE_AGP | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED, .load = via_driver_load, .unload = via_driver_unload, diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 0ef8e5481e15..139d859adf16 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -74,7 +74,6 @@ #include #define __OS_HAS_AGP (defined(CONFIG_AGP) || (defined(CONFIG_AGP_MODULE) && defined(MODULE))) -#define __OS_HAS_MTRR (defined(CONFIG_MTRR)) struct module; @@ -139,7 +138,6 @@ int drm_err(const char *func, const char *format, ...); /* driver capabilities and requirements mask */ #define DRIVER_USE_AGP 0x1 #define DRIVER_REQUIRE_AGP 0x2 -#define DRIVER_USE_MTRR 0x4 #define DRIVER_PCI_DMA 0x8 #define DRIVER_SG 0x10 #define DRIVER_HAVE_DMA 0x20 @@ -1216,15 +1214,6 @@ static inline int drm_dev_to_irq(struct drm_device *dev) return dev->driver->bus->get_irq(dev); } -#if __OS_HAS_MTRR -static inline int drm_core_has_MTRR(struct drm_device *dev) -{ - return drm_core_check_feature(dev, DRIVER_USE_MTRR); -} -#else -#define drm_core_has_MTRR(dev) (0) -#endif - static inline void drm_device_set_unplugged(struct drm_device *dev) { smp_wmb(); -- cgit v1.2.3-71-gd317 From 6eb9278adabd17da3bc1cb843c729d1b10d79c93 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Aug 2013 15:41:29 +0200 Subject: drm: remove the dma_ioctl special-case We might as well have a real ioctl function which checks for the callbacks. This seems to be a remnant from back in the days when each drm driver had their own complete ioctl table, with no shared core drm table at all. To make really sure no mis-guided user in a kms driver pops up again explicitly check for that in the new ioctl implementation. v2: Drop the unused variable I've accidentally left in the code, spotted by David Herrmann. Cc: David Herrmann Signed-off-by: Daniel Vetter Reviewed-by: David Herrmann Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_bufs.c | 12 ++++++++++++ drivers/gpu/drm/drm_drv.c | 6 +----- include/drm/drmP.h | 2 ++ 3 files changed, 15 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index f63133b0a9ab..471e051d295e 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -1455,6 +1455,18 @@ int drm_mapbufs(struct drm_device *dev, void *data, return retcode; } +int drm_dma_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + if (dev->driver->dma_ioctl) + return dev->driver->dma_ioctl(dev, data, file_priv); + else + return -EINVAL; +} + struct drm_local_map *drm_getsarea(struct drm_device *dev) { struct drm_map_list *entry; diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index d97976cc51cd..357a14ea3cb0 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -106,8 +106,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_INFO_BUFS, drm_infobufs, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_MAP_BUFS, drm_mapbufs, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_FREE_BUFS, drm_freebufs, DRM_AUTH), - /* The DRM_IOCTL_DMA ioctl should be defined by the driver. */ - DRM_IOCTL_DEF(DRM_IOCTL_DMA, NULL, DRM_AUTH), + DRM_IOCTL_DEF(DRM_IOCTL_DMA, drm_dma_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_IOCTL_CONTROL, drm_control, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), @@ -397,9 +396,6 @@ long drm_ioctl(struct file *filp, /* Do not trust userspace, use our own definition */ func = ioctl->func; - /* is there a local override? */ - if ((nr == DRM_IOCTL_NR(DRM_IOCTL_DMA)) && dev->driver->dma_ioctl) - func = dev->driver->dma_ioctl; if (!func) { DRM_DEBUG("no function\n"); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 139d859adf16..808eb237be0e 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1355,6 +1355,8 @@ extern int drm_freebufs(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mapbufs(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int drm_dma_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); /* DMA support (drm_dma.h) */ extern int drm_legacy_dma_setup(struct drm_device *dev); -- cgit v1.2.3-71-gd317 From cb6458f97b53d7f73043206c18014b3ca63ac345 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Aug 2013 15:41:34 +0200 Subject: drm: remove procfs code, take 2 So almost two years ago I've tried to nuke the procfs code already once before: http://lists.freedesktop.org/archives/dri-devel/2011-October/015707.html The conclusion was that userspace drivers (specifically libdrm device node detection) stopped relying on procfs in 2001. But after some digging it turned out that the drmstat tool in libdrm is still using those files (but only when certain options are set). So we've decided to keep profcs. But I when I've started to dig around again what exactly this tool does I've noticed that it tries to read the "mem", "vm", and "vma" files from procfs. Now as far my git history digging shows "mem" never did anything useful (at least in the version that first showed up in upstream history in 2004) and the file was remove in commit 955b12def42e83287c1bdb1411d99451753c1391 Author: Ben Gamari Date: Tue Feb 17 20:08:49 2009 -0500 drm: Convert proc files to seq_file and introduce debugfs Which means that for over 4 years drmstat has been broken, and no one cared. In my opinion that's proof enough that no one is actually using drmstat, and so that we can savely nuke the procfs support from drm. While at it fix up the error case cleanup for debugfs in drm_get_minor. v2: Fix dates, libdrm stopped relying on procfs for drm node detection in 2001. v3: fixup compilation warning for !CONFIG_DEBUG_FS, reported by Fengguang Wu. Cc: kbuild test robot Cc: Dave Airlie Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_drv.c | 8 -- drivers/gpu/drm/drm_proc.c | 209 --------------------------------------------- drivers/gpu/drm/drm_stub.c | 32 ++----- include/drm/drmP.h | 8 -- 5 files changed, 9 insertions(+), 250 deletions(-) delete mode 100644 drivers/gpu/drm/drm_proc.c (limited to 'include') diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 2aaf082368a2..7b2343a2f5eb 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -7,7 +7,7 @@ ccflags-y := -Iinclude/drm drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_context.o drm_dma.o \ drm_drv.o drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \ - drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \ + drm_lock.o drm_memory.o drm_stub.o drm_vm.o \ drm_agpsupport.o drm_scatter.o drm_pci.o \ drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \ drm_crtc.o drm_modes.o drm_edid.o \ diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 357a14ea3cb0..cf40be42baf6 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -238,13 +238,6 @@ static int __init drm_core_init(void) goto err_p2; } - drm_proc_root = proc_mkdir("dri", NULL); - if (!drm_proc_root) { - DRM_ERROR("Cannot create /proc/dri\n"); - ret = -1; - goto err_p3; - } - drm_debugfs_root = debugfs_create_dir("dri", NULL); if (!drm_debugfs_root) { DRM_ERROR("Cannot create /sys/kernel/debug/dri\n"); @@ -267,7 +260,6 @@ err_p1: static void __exit drm_core_exit(void) { - remove_proc_entry("dri", NULL); debugfs_remove(drm_debugfs_root); drm_sysfs_destroy(); diff --git a/drivers/gpu/drm/drm_proc.c b/drivers/gpu/drm/drm_proc.c deleted file mode 100644 index d7f2324b4fb1..000000000000 --- a/drivers/gpu/drm/drm_proc.c +++ /dev/null @@ -1,209 +0,0 @@ -/** - * \file drm_proc.c - * /proc support for DRM - * - * \author Rickard E. (Rik) Faith - * \author Gareth Hughes - * - * \par Acknowledgements: - * Matthew J Sottek sent in a patch to fix - * the problem with the proc files not outputting all their information. - */ - -/* - * Created: Mon Jan 11 09:48:47 1999 by faith@valinux.com - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include - -/*************************************************** - * Initialization, etc. - **************************************************/ - -/** - * Proc file list. - */ -static const struct drm_info_list drm_proc_list[] = { - {"name", drm_name_info, 0}, - {"vm", drm_vm_info, 0}, - {"clients", drm_clients_info, 0}, - {"bufs", drm_bufs_info, 0}, - {"gem_names", drm_gem_name_info, DRIVER_GEM}, -#if DRM_DEBUG_CODE - {"vma", drm_vma_info, 0}, -#endif -}; -#define DRM_PROC_ENTRIES ARRAY_SIZE(drm_proc_list) - -static int drm_proc_open(struct inode *inode, struct file *file) -{ - struct drm_info_node* node = PDE_DATA(inode); - - return single_open(file, node->info_ent->show, node); -} - -static const struct file_operations drm_proc_fops = { - .owner = THIS_MODULE, - .open = drm_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - - -/** - * Initialize a given set of proc files for a device - * - * \param files The array of files to create - * \param count The number of files given - * \param root DRI proc dir entry. - * \param minor device minor number - * \return Zero on success, non-zero on failure - * - * Create a given set of proc files represented by an array of - * gdm_proc_lists in the given root directory. - */ -static int drm_proc_create_files(const struct drm_info_list *files, int count, - struct proc_dir_entry *root, struct drm_minor *minor) -{ - struct drm_device *dev = minor->dev; - struct proc_dir_entry *ent; - struct drm_info_node *tmp; - int i; - - for (i = 0; i < count; i++) { - u32 features = files[i].driver_features; - - if (features != 0 && - (dev->driver->driver_features & features) != features) - continue; - - tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL); - if (!tmp) - return -1; - - tmp->minor = minor; - tmp->info_ent = &files[i]; - list_add(&tmp->list, &minor->proc_nodes.list); - - ent = proc_create_data(files[i].name, S_IRUGO, root, - &drm_proc_fops, tmp); - if (!ent) { - DRM_ERROR("Cannot create /proc/dri/%u/%s\n", - minor->index, files[i].name); - list_del(&tmp->list); - kfree(tmp); - return -1; - } - } - return 0; -} - -/** - * Initialize the DRI proc filesystem for a device - * - * \param dev DRM device - * \param root DRI proc dir entry. - * \param dev_root resulting DRI device proc dir entry. - * \return root entry pointer on success, or NULL on failure. - * - * Create the DRI proc root entry "/proc/dri", the device proc root entry - * "/proc/dri/%minor%/", and each entry in proc_list as - * "/proc/dri/%minor%/%name%". - */ -int drm_proc_init(struct drm_minor *minor, struct proc_dir_entry *root) -{ - char name[12]; - int ret; - - INIT_LIST_HEAD(&minor->proc_nodes.list); - sprintf(name, "%u", minor->index); - minor->proc_root = proc_mkdir(name, root); - if (!minor->proc_root) { - DRM_ERROR("Cannot create /proc/dri/%s\n", name); - return -1; - } - - ret = drm_proc_create_files(drm_proc_list, DRM_PROC_ENTRIES, - minor->proc_root, minor); - if (ret) { - remove_proc_subtree(name, root); - minor->proc_root = NULL; - DRM_ERROR("Failed to create core drm proc files\n"); - return ret; - } - - return 0; -} - -static int drm_proc_remove_files(const struct drm_info_list *files, int count, - struct drm_minor *minor) -{ - struct list_head *pos, *q; - struct drm_info_node *tmp; - int i; - - for (i = 0; i < count; i++) { - list_for_each_safe(pos, q, &minor->proc_nodes.list) { - tmp = list_entry(pos, struct drm_info_node, list); - if (tmp->info_ent == &files[i]) { - remove_proc_entry(files[i].name, - minor->proc_root); - list_del(pos); - kfree(tmp); - } - } - } - return 0; -} - -/** - * Cleanup the proc filesystem resources. - * - * \param minor device minor number. - * \param root DRI proc dir entry. - * \param dev_root DRI device proc dir entry. - * \return always zero. - * - * Remove all proc entries created by proc_init(). - */ -int drm_proc_cleanup(struct drm_minor *minor, struct proc_dir_entry *root) -{ - char name[64]; - - if (!root || !minor->proc_root) - return 0; - - drm_proc_remove_files(drm_proc_list, DRM_PROC_ENTRIES, minor); - - sprintf(name, "%d", minor->index); - remove_proc_subtree(name, root); - return 0; -} - diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 5edf938bbcc3..e30bb0d7c67a 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -68,7 +68,6 @@ module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); struct idr drm_minors_idr; struct class *drm_class; -struct proc_dir_entry *drm_proc_root; struct dentry *drm_debugfs_root; int drm_err(const char *func, const char *format, ...) @@ -315,9 +314,8 @@ EXPORT_SYMBOL(drm_fill_in_dev); * \param sec-minor structure to hold the assigned minor * \return negative number on failure. * - * Search an empty entry and initialize it to the given parameters, and - * create the proc init entry via proc_init(). This routines assigns - * minor numbers to secondary heads of multi-headed cards + * Search an empty entry and initialize it to the given parameters. This + * routines assigns minor numbers to secondary heads of multi-headed cards */ int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type) { @@ -345,20 +343,11 @@ int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type) idr_replace(&drm_minors_idr, new_minor, minor_id); - if (type == DRM_MINOR_LEGACY) { - ret = drm_proc_init(new_minor, drm_proc_root); - if (ret) { - DRM_ERROR("DRM: Failed to initialize /proc/dri.\n"); - goto err_mem; - } - } else - new_minor->proc_root = NULL; - #if defined(CONFIG_DEBUG_FS) ret = drm_debugfs_init(new_minor, minor_id, drm_debugfs_root); if (ret) { DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n"); - goto err_g2; + goto err_mem; } #endif @@ -366,7 +355,7 @@ int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type) if (ret) { printk(KERN_ERR "DRM: Error sysfs_device_add.\n"); - goto err_g2; + goto err_debugfs; } *minor = new_minor; @@ -374,10 +363,11 @@ int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type) return 0; -err_g2: - if (new_minor->type == DRM_MINOR_LEGACY) - drm_proc_cleanup(new_minor, drm_proc_root); +err_debugfs: +#if defined(CONFIG_DEBUG_FS) + drm_debugfs_cleanup(new_minor); err_mem: +#endif kfree(new_minor); err_idr: idr_remove(&drm_minors_idr, minor_id); @@ -391,10 +381,6 @@ EXPORT_SYMBOL(drm_get_minor); * * \param sec_minor - structure to be released * \return always zero - * - * Cleans up the proc resources. Not legal for this to be the - * last minor released. - * */ int drm_put_minor(struct drm_minor **minor_p) { @@ -402,8 +388,6 @@ int drm_put_minor(struct drm_minor **minor_p) DRM_DEBUG("release secondary minor %d\n", minor->index); - if (minor->type == DRM_MINOR_LEGACY) - drm_proc_cleanup(minor, drm_proc_root); #if defined(CONFIG_DEBUG_FS) drm_debugfs_cleanup(minor); #endif diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 808eb237be0e..39911dca359b 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -1027,8 +1026,6 @@ struct drm_minor { struct device kdev; /**< Linux device */ struct drm_device *dev; - struct proc_dir_entry *proc_root; /**< proc directory entry */ - struct drm_info_node proc_nodes; struct dentry *debugfs_root; struct list_head debugfs_list; @@ -1438,17 +1435,12 @@ extern unsigned int drm_timestamp_precision; extern unsigned int drm_timestamp_monotonic; extern struct class *drm_class; -extern struct proc_dir_entry *drm_proc_root; extern struct dentry *drm_debugfs_root; extern struct idr drm_minors_idr; extern struct drm_local_map *drm_getsarea(struct drm_device *dev); - /* Proc support (drm_proc.h) */ -extern int drm_proc_init(struct drm_minor *minor, struct proc_dir_entry *root); -extern int drm_proc_cleanup(struct drm_minor *minor, struct proc_dir_entry *root); - /* Debugfs support */ #if defined(CONFIG_DEBUG_FS) extern int drm_debugfs_init(struct drm_minor *minor, int minor_id, -- cgit v1.2.3-71-gd317 From b1baec525e75bcf4dd8c8824cfde476a98b13bab Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Tue, 16 Jul 2013 17:28:46 +0800 Subject: dma: mxs-dma: remove code left from generic DMA binding conversion With all mxs-dma clients moved to use generic DMA helper, the code left from generic DMA binding conversion can be removed now. Signed-off-by: Shawn Guo Reviewed-by: Arnd Bergmann Signed-off-by: Vinod Koul --- drivers/dma/mxs-dma.c | 23 ----------------------- include/linux/fsl/mxs-dma.h | 20 -------------------- 2 files changed, 43 deletions(-) delete mode 100644 include/linux/fsl/mxs-dma.h (limited to 'include') diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 6fb4b7bc448f..ccd13df841db 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -197,24 +196,6 @@ static struct mxs_dma_chan *to_mxs_dma_chan(struct dma_chan *chan) return container_of(chan, struct mxs_dma_chan, chan); } -int mxs_dma_is_apbh(struct dma_chan *chan) -{ - struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); - struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; - - return dma_is_apbh(mxs_dma); -} -EXPORT_SYMBOL_GPL(mxs_dma_is_apbh); - -int mxs_dma_is_apbx(struct dma_chan *chan) -{ - struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); - struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; - - return !dma_is_apbh(mxs_dma); -} -EXPORT_SYMBOL_GPL(mxs_dma_is_apbx); - static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan) { struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; @@ -349,13 +330,9 @@ static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id) static int mxs_dma_alloc_chan_resources(struct dma_chan *chan) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); - struct mxs_dma_data *data = chan->private; struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; int ret; - if (data) - mxs_chan->chan_irq = data->chan_irq; - mxs_chan->ccw = dma_alloc_coherent(mxs_dma->dma_device.dev, CCW_BLOCK_SIZE, &mxs_chan->ccw_phys, GFP_KERNEL); diff --git a/include/linux/fsl/mxs-dma.h b/include/linux/fsl/mxs-dma.h deleted file mode 100644 index 55d870238399..000000000000 --- a/include/linux/fsl/mxs-dma.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __MACH_MXS_DMA_H__ -#define __MACH_MXS_DMA_H__ - -#include - -struct mxs_dma_data { - int chan_irq; -}; - -extern int mxs_dma_is_apbh(struct dma_chan *chan); -extern int mxs_dma_is_apbx(struct dma_chan *chan); -#endif /* __MACH_MXS_DMA_H__ */ -- cgit v1.2.3-71-gd317 From bcbb15530ebe9737622cb7779b35c61f48b49734 Mon Sep 17 00:00:00 2001 From: Tim Gardner Date: Fri, 16 Aug 2013 11:18:59 -0600 Subject: ALSA: pcm: Add snd_printd_ratelimit() Direct calls to printk_limit() will emit log noise even when CONFIG_SND_DEBUG is not defined. Add a wrapper macro around printk_limit() that is conditionally defined by CONFIG_SND_DEBUG. Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: Pierre-Louis Bossart Cc: Lars-Peter Clausen Cc: Yacine Belkadi Signed-off-by: Tim Gardner Signed-off-by: Takashi Iwai --- include/sound/core.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/sound/core.h b/include/sound/core.h index c586617cfa0d..2a14f1f02d4f 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -27,6 +27,7 @@ #include /* struct rw_semaphore */ #include /* pm_message_t */ #include +#include /* number of supported soundcards */ #ifdef CONFIG_SND_DYNAMIC_MINORS @@ -375,6 +376,11 @@ void __snd_printk(unsigned int level, const char *file, int line, */ #define snd_BUG() WARN(1, "BUG?\n") +/** + * Suppress high rates of output when CONFIG_SND_DEBUG is enabled. + */ +#define snd_printd_ratelimit() printk_ratelimit() + /** * snd_BUG_ON - debugging check macro * @cond: condition to evaluate @@ -398,6 +404,8 @@ static inline void _snd_printd(int level, const char *format, ...) {} unlikely(__ret_warn_on); \ }) +static inline bool snd_printd_ratelimit(void) { return false; } + #endif /* CONFIG_SND_DEBUG */ #ifdef CONFIG_SND_DEBUG_VERBOSE -- cgit v1.2.3-71-gd317 From 1cb650b91ba582f6737457b7d22e368585596d2c Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Mon, 19 Aug 2013 10:05:24 +0800 Subject: cgroup: change cgroup_from_id() to css_from_id() Now we want cgroup core to always provide the css to use to the subsystems, so change this API to css_from_id(). Uninline css_from_id(), because it's getting bigger and cgroup_css() has been unexported. While at it, remove the #ifdef, and shuffle the order of the args. Signed-off-by: Li Zefan Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 20 ++------------------ kernel/cgroup.c | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index c24bd0b9f93a..b685955d4b29 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -741,27 +741,11 @@ static inline struct cgroup *task_cgroup(struct task_struct *task, return task_css(task, subsys_id)->cgroup; } -/** - * cgroup_from_id - lookup cgroup by id - * @ss: cgroup subsys to be looked into - * @id: the cgroup id - * - * Returns the cgroup if there's valid one with @id, otherwise returns NULL. - * Should be called under rcu_read_lock(). - */ -static inline struct cgroup *cgroup_from_id(struct cgroup_subsys *ss, int id) -{ -#ifdef CONFIG_PROVE_RCU - rcu_lockdep_assert(rcu_read_lock_held() || - lockdep_is_held(&cgroup_mutex), - "cgroup_from_id() needs proper protection"); -#endif - return idr_find(&ss->root->cgroup_idr, id); -} - struct cgroup_subsys_state *css_next_child(struct cgroup_subsys_state *pos, struct cgroup_subsys_state *parent); +struct cgroup_subsys_state *css_from_id(int id, struct cgroup_subsys *ss); + /** * css_for_each_child - iterate through children of a css * @pos: the css * to use as the loop cursor diff --git a/kernel/cgroup.c b/kernel/cgroup.c index b69b572131e5..ff7d642a070a 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -5717,6 +5717,28 @@ struct cgroup_subsys_state *cgroup_css_from_dir(struct file *f, int id) return css ? css : ERR_PTR(-ENOENT); } +/** + * css_from_id - lookup css by id + * @id: the cgroup id + * @ss: cgroup subsys to be looked into + * + * Returns the css if there's valid one with @id, otherwise returns NULL. + * Should be called under rcu_read_lock(). + */ +struct cgroup_subsys_state *css_from_id(int id, struct cgroup_subsys *ss) +{ + struct cgroup *cgrp; + + rcu_lockdep_assert(rcu_read_lock_held() || + lockdep_is_held(&cgroup_mutex), + "css_from_id() needs proper protection"); + + cgrp = idr_find(&ss->root->cgroup_idr, id); + if (cgrp) + return cgroup_css(cgrp, ss->subsys_id); + return NULL; +} + #ifdef CONFIG_CGROUP_DEBUG static struct cgroup_subsys_state * debug_css_alloc(struct cgroup_subsys_state *parent_css) -- cgit v1.2.3-71-gd317 From 2f74f972d4cc7d83408ea0c32d424edcb44887bf Mon Sep 17 00:00:00 2001 From: Harshula Jayasuriya Date: Fri, 16 Aug 2013 03:46:40 +1000 Subject: sunrpc: prepare NFS for 2038 1) The kernel sunrpc code needs to handle seconds since epoch greater than 2147483647. This means functions that parse time as an int need to handle it as time_t. 2) The kernel changes must be accompanied by userspace changes in nfs-utils. Signed-off-by: Harshula Jayasuriya Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/cache.h | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index 6ce690de447f..437ddb6c4aef 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h @@ -264,12 +264,30 @@ static inline int get_uint(char **bpp, unsigned int *anint) return 0; } +static inline int get_time(char **bpp, time_t *time) +{ + char buf[50]; + long long ll; + int len = qword_get(bpp, buf, sizeof(buf)); + + if (len < 0) + return -EINVAL; + if (len == 0) + return -ENOENT; + + if (kstrtoll(buf, 0, &ll)) + return -EINVAL; + + *time = (time_t)ll; + return 0; +} + static inline time_t get_expiry(char **bpp) { - int rv; + time_t rv; struct timespec boot; - if (get_int(bpp, &rv)) + if (get_time(bpp, &rv)) return 0; if (rv < 0) return 0; -- cgit v1.2.3-71-gd317 From b3b515bbd689ba3937cac2dd3fc55057f8c50329 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:52:47 -0700 Subject: pstore: Add new argument 'compressed' in pstore write callback Addition of new argument 'compressed' in the write call back will help the backend to know if the data passed from pstore is compressed or not (In case where compression fails.). If compressed, the backend can add a tag indicating the data is compressed while writing to persistent store. Signed-off-by: Aruna Balakrishnaiah Reviewed-by: Kees Cook Signed-off-by: Tony Luck --- arch/powerpc/platforms/pseries/nvram.c | 4 ++-- drivers/acpi/apei/erst.c | 4 ++-- drivers/firmware/efi/efi-pstore.c | 2 +- fs/pstore/platform.c | 7 ++++--- fs/pstore/ram.c | 2 +- include/linux/pstore.h | 4 ++-- 6 files changed, 12 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index b966458e9b21..dbe5dadb5fcc 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -554,7 +554,7 @@ static int nvram_pstore_open(struct pstore_info *psi) * @part: pstore writes data to registered buffer in parts, * part number will indicate the same. * @count: Indicates oops count - * @hsize: Size of header added by pstore + * @compressed: Flag to indicate the log is compressed * @size: number of bytes written to the registered buffer * @psi: registered pstore_info structure * @@ -565,7 +565,7 @@ static int nvram_pstore_open(struct pstore_info *psi) static int nvram_pstore_write(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, int count, - size_t hsize, size_t size, + bool compressed, size_t size, struct pstore_info *psi) { int rc; diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 6885809abcd8..bcdf8cefcb9e 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -937,7 +937,7 @@ static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, struct pstore_info *psi); static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, - u64 *id, unsigned int part, int count, size_t hsize, + u64 *id, unsigned int part, int count, bool compressed, size_t size, struct pstore_info *psi); static int erst_clearer(enum pstore_type_id type, u64 id, int count, struct timespec time, struct pstore_info *psi); @@ -1057,7 +1057,7 @@ out: } static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, - u64 *id, unsigned int part, int count, size_t hsize, + u64 *id, unsigned int part, int count, bool compressed, size_t size, struct pstore_info *psi) { struct cper_pstore_record *rcd = (struct cper_pstore_record *) diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 73de5a9c2247..fab6892f7053 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -103,7 +103,7 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, static int efi_pstore_write(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, - unsigned int part, int count, size_t hsize, size_t size, + unsigned int part, int count, bool compressed, size_t size, struct pstore_info *psi) { char name[DUMP_NAME_LEN]; diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 422962ae9fc2..20fa686f80fa 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -149,6 +149,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, unsigned long size; int hsize; size_t len; + bool compressed = false; dst = psinfo->buf; hsize = sprintf(dst, "%s#%d Part%d\n", why, oopscount, part); @@ -159,7 +160,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, break; ret = psinfo->write(PSTORE_TYPE_DMESG, reason, &id, part, - oopscount, hsize, hsize + len, psinfo); + oopscount, compressed, hsize + len, psinfo); if (ret == 0 && reason == KMSG_DUMP_OOPS && pstore_is_mounted()) pstore_new_entry = 1; @@ -221,10 +222,10 @@ static void pstore_register_console(void) {} static int pstore_write_compat(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, int count, - size_t hsize, size_t size, + bool compressed, size_t size, struct pstore_info *psi) { - return psi->write_buf(type, reason, id, part, psinfo->buf, hsize, + return psi->write_buf(type, reason, id, part, psinfo->buf, compressed, size, psi); } diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index a6119f9469e2..fe7188f742b7 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -196,7 +196,7 @@ static int notrace ramoops_pstore_write_buf(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, const char *buf, - size_t hsize, size_t size, + bool compressed, size_t size, struct pstore_info *psi) { struct ramoops_context *cxt = psi->data; diff --git a/include/linux/pstore.h b/include/linux/pstore.h index 4aa80ba830a2..abfca4f59422 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -58,11 +58,11 @@ struct pstore_info { struct pstore_info *psi); int (*write)(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, - unsigned int part, int count, size_t hsize, + unsigned int part, int count, bool compressed, size_t size, struct pstore_info *psi); int (*write_buf)(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, - unsigned int part, const char *buf, size_t hsize, + unsigned int part, const char *buf, bool compressed, size_t size, struct pstore_info *psi); int (*erase)(enum pstore_type_id type, u64 id, int count, struct timespec time, -- cgit v1.2.3-71-gd317 From 9a4e1398208d147a9240731a1f8cfe7d8cc4c553 Mon Sep 17 00:00:00 2001 From: Aruna Balakrishnaiah Date: Fri, 16 Aug 2013 13:53:19 -0700 Subject: pstore: Introduce new argument 'compressed' in the read callback Backends will set the flag 'compressed' after reading the log from persistent store to indicate the data being returned to pstore is compressed or not. Signed-off-by: Aruna Balakrishnaiah Reviewed-by: Kees Cook Signed-off-by: Tony Luck --- arch/powerpc/platforms/pseries/nvram.c | 2 +- drivers/acpi/apei/erst.c | 4 ++-- drivers/firmware/efi/efi-pstore.c | 3 ++- fs/pstore/platform.c | 4 +++- fs/pstore/ram.c | 3 ++- include/linux/pstore.h | 2 +- 6 files changed, 11 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index dbe5dadb5fcc..6c4dc52ac0b5 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -598,7 +598,7 @@ static int nvram_pstore_write(enum pstore_type_id type, */ static ssize_t nvram_pstore_read(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, - struct pstore_info *psi) + bool *compressed, struct pstore_info *psi) { struct oops_log_info *oops_hdr; unsigned int err_type, id_no, size = 0; diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index bcdf8cefcb9e..b412e9137e36 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -935,7 +935,7 @@ static int erst_open_pstore(struct pstore_info *psi); static int erst_close_pstore(struct pstore_info *psi); static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, - struct pstore_info *psi); + bool *compressed, struct pstore_info *psi); static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, int count, bool compressed, size_t size, struct pstore_info *psi); @@ -991,7 +991,7 @@ static int erst_close_pstore(struct pstore_info *psi) static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, - struct pstore_info *psi) + bool *compressed, struct pstore_info *psi) { int rc; ssize_t len = 0; diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index fab6892f7053..9a5425f3f885 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -87,7 +87,8 @@ static int efi_pstore_read_func(struct efivar_entry *entry, void *data) static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type, int *count, struct timespec *timespec, - char **buf, struct pstore_info *psi) + char **buf, bool *compressed, + struct pstore_info *psi) { struct pstore_read_data data; diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 56218cb35267..6418eb77d64b 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -428,6 +428,7 @@ void pstore_get_records(int quiet) enum pstore_type_id type; struct timespec time; int failed = 0, rc; + bool compressed; if (!psi) return; @@ -436,7 +437,8 @@ void pstore_get_records(int quiet) if (psi->open && psi->open(psi)) goto out; - while ((size = psi->read(&id, &type, &count, &time, &buf, psi)) > 0) { + while ((size = psi->read(&id, &type, &count, &time, &buf, &compressed, + psi)) > 0) { rc = pstore_mkfile(type, psi->name, id, count, buf, (size_t)size, time, psi); kfree(buf); diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index fe7188f742b7..292722327811 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -133,7 +133,8 @@ ramoops_get_next_prz(struct persistent_ram_zone *przs[], uint *c, uint max, static ssize_t ramoops_pstore_read(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, - char **buf, struct pstore_info *psi) + char **buf, bool *compressed, + struct pstore_info *psi) { ssize_t size; ssize_t ecc_notice_size; diff --git a/include/linux/pstore.h b/include/linux/pstore.h index abfca4f59422..abd437d0a8a7 100644 --- a/include/linux/pstore.h +++ b/include/linux/pstore.h @@ -55,7 +55,7 @@ struct pstore_info { int (*close)(struct pstore_info *psi); ssize_t (*read)(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, - struct pstore_info *psi); + bool *compressed, struct pstore_info *psi); int (*write)(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, int count, bool compressed, -- cgit v1.2.3-71-gd317 From b43dd416be21bc8ad60984e13def032f01aaaa18 Mon Sep 17 00:00:00 2001 From: Adrian Knoth Date: Mon, 19 Aug 2013 17:20:32 +0200 Subject: ALSA: hdspm - Fix SNDRV_HDSPM_IOCTL_GET_LTC Use struct hdspm_ltc to query the LTC, using a mixer struct is just plain wrong. Due to the wrong struct, this ioctl was never working, so we're free to fix it without breaking userspace compatibility. Signed-off-by: Adrian Knoth Signed-off-by: Takashi Iwai --- include/uapi/sound/hdspm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/sound/hdspm.h b/include/uapi/sound/hdspm.h index 1f59ea2a4a76..d956c3593f65 100644 --- a/include/uapi/sound/hdspm.h +++ b/include/uapi/sound/hdspm.h @@ -111,7 +111,7 @@ struct hdspm_ltc { enum hdspm_ltc_input_format input_format; }; -#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_mixer_ioctl) +#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_ltc) /** * The status data reflects the device's current state -- cgit v1.2.3-71-gd317 From 7ef3dcc8145263cb5a8c7059f82d44c948eb46a8 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 29 Jul 2013 12:24:58 +0100 Subject: clk: abstract parent cache Abstract access to the clock parent cache by defining clk_get_parent_by_index(clk, index). This allows access to parent clocks from clock drivers. Signed-off-by: James Hogan Reviewed-by: Stephen Boyd Cc: Mike Turquette Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 21 ++++++++++++++------- include/linux/clk-provider.h | 1 + 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 56a00db61332..42c15a8ba34d 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -559,6 +559,19 @@ struct clk *__clk_get_parent(struct clk *clk) return !clk ? NULL : clk->parent; } +struct clk *clk_get_parent_by_index(struct clk *clk, u8 index) +{ + if (!clk || index >= clk->num_parents) + return NULL; + else if (!clk->parents) + return __clk_lookup(clk->parent_names[index]); + else if (!clk->parents[index]) + return clk->parents[index] = + __clk_lookup(clk->parent_names[index]); + else + return clk->parents[index]; +} + unsigned int __clk_get_enable_count(struct clk *clk) { return !clk ? 0 : clk->enable_count; @@ -1316,13 +1329,7 @@ static struct clk *__clk_init_parent(struct clk *clk) kzalloc((sizeof(struct clk*) * clk->num_parents), GFP_KERNEL); - if (!clk->parents) - ret = __clk_lookup(clk->parent_names[index]); - else if (!clk->parents[index]) - ret = clk->parents[index] = - __clk_lookup(clk->parent_names[index]); - else - ret = clk->parents[index]; + ret = clk_get_parent_by_index(clk, index); out: return ret; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 9487b96939e8..a83409285a7f 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -420,6 +420,7 @@ const char *__clk_get_name(struct clk *clk); struct clk_hw *__clk_get_hw(struct clk *clk); u8 __clk_get_num_parents(struct clk *clk); struct clk *__clk_get_parent(struct clk *clk); +struct clk *clk_get_parent_by_index(struct clk *clk, u8 index); unsigned int __clk_get_enable_count(struct clk *clk); unsigned int __clk_get_prepare_count(struct clk *clk); unsigned long __clk_get_rate(struct clk *clk); -- cgit v1.2.3-71-gd317 From 71472c0c06cf9a3d1540762ea205654c584e3bc4 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 29 Jul 2013 12:25:00 +0100 Subject: clk: add support for clock reparent on set_rate Add core support to allow clock implementations to select the best parent clock when rounding a rate, e.g. the one which can provide the closest clock rate to that requested. This is by way of adding a new clock op, determine_rate(), which is like round_rate() but has an extra parameter to allow the clock implementation to optionally select a different parent clock. The core then takes care of reparenting the clock when setting the rate. The parent change takes place with the help of some new private data members. struct clk::new_parent specifies a clock's new parent (NULL indicates no change), and struct clk::new_child specifies a clock's new child (whose new_parent member points back to it). The purpose of these are to allow correct walking of the future tree for notifications prior to actually reparenting any clocks, specifically to skip child clocks who are being reparented to another clock (they will be notified via the new parent), and to include any new child clock. These pointers are set by clk_calc_subtree(), and the new_child pointer gets cleared when a child is actually reparented to avoid duplicate POST_RATE_CHANGE notifications. Each place where round_rate() is called, determine_rate() is checked first and called in preference. This restructures a few of the call sites to simplify the logic into if/else blocks. Signed-off-by: James Hogan Reviewed-by: Stephen Boyd Cc: Mike Turquette Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Mike Turquette --- Documentation/clk.txt | 46 ++++++++------ drivers/clk/clk.c | 142 +++++++++++++++++++++++++++++-------------- include/linux/clk-private.h | 3 + include/linux/clk-provider.h | 7 +++ 4 files changed, 132 insertions(+), 66 deletions(-) (limited to 'include') diff --git a/Documentation/clk.txt b/Documentation/clk.txt index 6f68ba0d1e01..3aeb5c440442 100644 --- a/Documentation/clk.txt +++ b/Documentation/clk.txt @@ -70,6 +70,10 @@ the operations defined in clk.h: unsigned long parent_rate); long (*round_rate)(struct clk_hw *hw, unsigned long, unsigned long *); + long (*determine_rate)(struct clk_hw *hw, + unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_clk); int (*set_parent)(struct clk_hw *hw, u8 index); u8 (*get_parent)(struct clk_hw *hw); int (*set_rate)(struct clk_hw *hw, unsigned long); @@ -179,26 +183,28 @@ mandatory, a cell marked as "n" implies that either including that callback is invalid or otherwise unnecessary. Empty cells are either optional or must be evaluated on a case-by-case basis. - clock hardware characteristics - ----------------------------------------------------------- - | gate | change rate | single parent | multiplexer | root | - |------|-------------|---------------|-------------|------| -.prepare | | | | | | -.unprepare | | | | | | - | | | | | | -.enable | y | | | | | -.disable | y | | | | | -.is_enabled | y | | | | | - | | | | | | -.recalc_rate | | y | | | | -.round_rate | | y | | | | -.set_rate | | y | | | | - | | | | | | -.set_parent | | | n | y | n | -.get_parent | | | n | y | n | - | | | | | | -.init | | | | | | - ----------------------------------------------------------- + clock hardware characteristics + ----------------------------------------------------------- + | gate | change rate | single parent | multiplexer | root | + |------|-------------|---------------|-------------|------| +.prepare | | | | | | +.unprepare | | | | | | + | | | | | | +.enable | y | | | | | +.disable | y | | | | | +.is_enabled | y | | | | | + | | | | | | +.recalc_rate | | y | | | | +.round_rate | | y [1] | | | | +.determine_rate | | y [1] | | | | +.set_rate | | y | | | | + | | | | | | +.set_parent | | | n | y | n | +.get_parent | | | n | y | n | + | | | | | | +.init | | | | | | + ----------------------------------------------------------- +[1] either one of round_rate or determine_rate is required. Finally, register your clock at run-time with a hardware-specific registration function. This function simply populates struct clk_foo's diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 3d57cf64c9e7..f33f1ccf1b2f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -889,21 +889,24 @@ EXPORT_SYMBOL_GPL(clk_enable); unsigned long __clk_round_rate(struct clk *clk, unsigned long rate) { unsigned long parent_rate = 0; + struct clk *parent; if (!clk) return 0; - if (!clk->ops->round_rate) { - if (clk->flags & CLK_SET_RATE_PARENT) - return __clk_round_rate(clk->parent, rate); - else - return clk->rate; - } - - if (clk->parent) - parent_rate = clk->parent->rate; - - return clk->ops->round_rate(clk->hw, rate, &parent_rate); + parent = clk->parent; + if (parent) + parent_rate = parent->rate; + + if (clk->ops->determine_rate) + return clk->ops->determine_rate(clk->hw, rate, &parent_rate, + &parent); + else if (clk->ops->round_rate) + return clk->ops->round_rate(clk->hw, rate, &parent_rate); + else if (clk->flags & CLK_SET_RATE_PARENT) + return __clk_round_rate(clk->parent, rate); + else + return clk->rate; } /** @@ -1056,6 +1059,10 @@ static u8 clk_fetch_parent_index(struct clk *clk, struct clk *parent) static void clk_reparent(struct clk *clk, struct clk *new_parent) { + /* avoid duplicate POST_RATE_CHANGE notifications */ + if (new_parent->new_child == clk) + new_parent->new_child = NULL; + hlist_del(&clk->child_node); if (new_parent) @@ -1176,18 +1183,25 @@ out: return ret; } -static void clk_calc_subtree(struct clk *clk, unsigned long new_rate) +static void clk_calc_subtree(struct clk *clk, unsigned long new_rate, + struct clk *new_parent, u8 p_index) { struct clk *child; clk->new_rate = new_rate; + clk->new_parent = new_parent; + clk->new_parent_index = p_index; + /* include clk in new parent's PRE_RATE_CHANGE notifications */ + clk->new_child = NULL; + if (new_parent && new_parent != clk->parent) + new_parent->new_child = clk; hlist_for_each_entry(child, &clk->children, child_node) { if (child->ops->recalc_rate) child->new_rate = child->ops->recalc_rate(child->hw, new_rate); else child->new_rate = new_rate; - clk_calc_subtree(child, child->new_rate); + clk_calc_subtree(child, child->new_rate, NULL, 0); } } @@ -1198,50 +1212,63 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate) static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) { struct clk *top = clk; + struct clk *old_parent, *parent; unsigned long best_parent_rate = 0; unsigned long new_rate; + u8 p_index = 0; /* sanity */ if (IS_ERR_OR_NULL(clk)) return NULL; /* save parent rate, if it exists */ - if (clk->parent) - best_parent_rate = clk->parent->rate; - - /* never propagate up to the parent */ - if (!(clk->flags & CLK_SET_RATE_PARENT)) { - if (!clk->ops->round_rate) { - clk->new_rate = clk->rate; - return NULL; - } - new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); + parent = old_parent = clk->parent; + if (parent) + best_parent_rate = parent->rate; + + /* find the closest rate and parent clk/rate */ + if (clk->ops->determine_rate) { + new_rate = clk->ops->determine_rate(clk->hw, rate, + &best_parent_rate, + &parent); + } else if (clk->ops->round_rate) { + new_rate = clk->ops->round_rate(clk->hw, rate, + &best_parent_rate); + } else if (!parent || !(clk->flags & CLK_SET_RATE_PARENT)) { + /* pass-through clock without adjustable parent */ + clk->new_rate = clk->rate; + return NULL; + } else { + /* pass-through clock with adjustable parent */ + top = clk_calc_new_rates(parent, rate); + new_rate = parent->new_rate; goto out; } - /* need clk->parent from here on out */ - if (!clk->parent) { - pr_debug("%s: %s has NULL parent\n", __func__, clk->name); + /* some clocks must be gated to change parent */ + if (parent != old_parent && + (clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) { + pr_debug("%s: %s not gated but wants to reparent\n", + __func__, clk->name); return NULL; } - if (!clk->ops->round_rate) { - top = clk_calc_new_rates(clk->parent, rate); - new_rate = clk->parent->new_rate; - - goto out; + /* try finding the new parent index */ + if (parent) { + p_index = clk_fetch_parent_index(clk, parent); + if (p_index == clk->num_parents) { + pr_debug("%s: clk %s can not be parent of clk %s\n", + __func__, parent->name, clk->name); + return NULL; + } } - new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); - - if (best_parent_rate != clk->parent->rate) { - top = clk_calc_new_rates(clk->parent, best_parent_rate); - - goto out; - } + if ((clk->flags & CLK_SET_RATE_PARENT) && parent && + best_parent_rate != parent->rate) + top = clk_calc_new_rates(parent, best_parent_rate); out: - clk_calc_subtree(clk, new_rate); + clk_calc_subtree(clk, new_rate, parent, p_index); return top; } @@ -1253,7 +1280,7 @@ out: */ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long event) { - struct clk *child, *fail_clk = NULL; + struct clk *child, *tmp_clk, *fail_clk = NULL; int ret = NOTIFY_DONE; if (clk->rate == clk->new_rate) @@ -1266,9 +1293,19 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even } hlist_for_each_entry(child, &clk->children, child_node) { - clk = clk_propagate_rate_change(child, event); - if (clk) - fail_clk = clk; + /* Skip children who will be reparented to another clock */ + if (child->new_parent && child->new_parent != clk) + continue; + tmp_clk = clk_propagate_rate_change(child, event); + if (tmp_clk) + fail_clk = tmp_clk; + } + + /* handle the new child who might not be in clk->children yet */ + if (clk->new_child) { + tmp_clk = clk_propagate_rate_change(clk->new_child, event); + if (tmp_clk) + fail_clk = tmp_clk; } return fail_clk; @@ -1286,6 +1323,10 @@ static void clk_change_rate(struct clk *clk) old_rate = clk->rate; + /* set parent */ + if (clk->new_parent && clk->new_parent != clk->parent) + __clk_set_parent(clk, clk->new_parent, clk->new_parent_index); + if (clk->parent) best_parent_rate = clk->parent->rate; @@ -1300,8 +1341,16 @@ static void clk_change_rate(struct clk *clk) if (clk->notifier_count && old_rate != clk->rate) __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &clk->children, child_node) { + /* Skip children who will be reparented to another clock */ + if (child->new_parent && child->new_parent != clk) + continue; clk_change_rate(child); + } + + /* handle the new child who might not be in clk->children yet */ + if (clk->new_child) + clk_change_rate(clk->new_child); } /** @@ -1552,8 +1601,9 @@ int __clk_init(struct device *dev, struct clk *clk) /* check that clk_ops are sane. See Documentation/clk.txt */ if (clk->ops->set_rate && - !(clk->ops->round_rate && clk->ops->recalc_rate)) { - pr_warning("%s: %s must implement .round_rate & .recalc_rate\n", + !((clk->ops->round_rate || clk->ops->determine_rate) && + clk->ops->recalc_rate)) { + pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n", __func__, clk->name); ret = -EINVAL; goto out; diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h index dd7adff76e81..8138c94409f3 100644 --- a/include/linux/clk-private.h +++ b/include/linux/clk-private.h @@ -33,8 +33,11 @@ struct clk { const char **parent_names; struct clk **parents; u8 num_parents; + u8 new_parent_index; unsigned long rate; unsigned long new_rate; + struct clk *new_parent; + struct clk *new_child; unsigned long flags; unsigned int enable_count; unsigned int prepare_count; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index a83409285a7f..4f525b37c6fd 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -79,6 +79,10 @@ struct clk_hw; * @round_rate: Given a target rate as input, returns the closest rate actually * supported by the clock. * + * @determine_rate: Given a target rate as input, returns the closest rate + * actually supported by the clock, and optionally the parent clock + * that should be used to provide the clock rate. + * * @get_parent: Queries the hardware to determine the parent of a clock. The * return value is a u8 which specifies the index corresponding to * the parent clock. This index can be applied to either the @@ -126,6 +130,9 @@ struct clk_ops { unsigned long parent_rate); long (*round_rate)(struct clk_hw *hw, unsigned long, unsigned long *); + long (*determine_rate)(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_clk); int (*set_parent)(struct clk_hw *hw, u8 index); u8 (*get_parent)(struct clk_hw *hw); int (*set_rate)(struct clk_hw *hw, unsigned long, -- cgit v1.2.3-71-gd317 From 819c1de344c5b8350bffd35be9a0fa74541292d3 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 29 Jul 2013 12:25:01 +0100 Subject: clk: add CLK_SET_RATE_NO_REPARENT flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a CLK_SET_RATE_NO_REPARENT clock flag, which will prevent muxes being reparented during clk_set_rate. To avoid breaking existing platforms, all callers of clk_register_mux() are adjusted to pass the new flag. Platform maintainers are encouraged to remove the flag if they wish to allow mux reparenting on set_rate. Signed-off-by: James Hogan Reviewed-by: Stephen Boyd Cc: Mike Turquette Cc: Russell King Cc: Sascha Hauer Cc: Stephen Warren Cc: Viresh Kumar Cc: Kukjin Kim Cc: Haojian Zhuang Cc: Chao Xie Cc: Arnd Bergmann Cc: "Emilio López" Cc: Gregory CLEMENT Cc: Maxime Ripard Cc: Prashant Gaikwad Cc: Thierry Reding Cc: Peter De Schrijver Cc: Pawel Moll Cc: Catalin Marinas Cc: Andrew Chew Cc: Doug Anderson Cc: Heiko Stuebner Cc: Paul Walmsley Cc: Sylwester Nawrocki Cc: Thomas Abraham Cc: Tomasz Figa Cc: linux-arm-kernel@lists.infradead.org Cc: linux-samsung-soc@vger.kernel.org Cc: spear-devel@list.st.com Cc: linux-tegra@vger.kernel.org Tested-by: Haojian Zhuang Acked-by: Stephen Warren [tegra] Acked-by: Maxime Ripard [sunxi] Acked-by: Sören Brinkmann [Zynq] Signed-off-by: Mike Turquette --- arch/arm/mach-imx/clk.h | 5 +- drivers/clk/mmp/clk-mmp2.c | 39 ++++--- drivers/clk/mmp/clk-pxa168.c | 40 +++++--- drivers/clk/mmp/clk-pxa910.c | 31 ++++-- drivers/clk/mxs/clk.h | 4 +- drivers/clk/samsung/clk-exynos-audss.c | 6 +- drivers/clk/samsung/clk.h | 2 +- drivers/clk/spear/spear1310_clock.c | 179 +++++++++++++++++---------------- drivers/clk/spear/spear1340_clock.c | 97 ++++++++++-------- drivers/clk/spear/spear3xx_clock.c | 57 +++++++---- drivers/clk/spear/spear6xx_clock.c | 35 ++++--- drivers/clk/sunxi/clk-sunxi.c | 3 +- drivers/clk/tegra/clk-tegra114.c | 36 ++++--- drivers/clk/tegra/clk-tegra20.c | 6 +- drivers/clk/tegra/clk-tegra30.c | 33 ++++-- drivers/clk/versatile/clk-vexpress.c | 4 +- drivers/clk/zynq/clkc.c | 86 +++++++++------- include/linux/clk-provider.h | 1 + 18 files changed, 388 insertions(+), 276 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index 0e4e8bb261b9..c8575fb7f717 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -79,7 +79,8 @@ static inline struct clk *imx_clk_gate(const char *name, const char *parent, static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, u8 shift, u8 width, const char **parents, int num_parents) { - return clk_register_mux(NULL, name, parents, num_parents, 0, reg, shift, + return clk_register_mux(NULL, name, parents, num_parents, + CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0, &imx_ccm_lock); } @@ -88,7 +89,7 @@ static inline struct clk *imx_clk_mux_flags(const char *name, int num_parents, unsigned long flags) { return clk_register_mux(NULL, name, parents, num_parents, - flags, reg, shift, width, 0, + flags | CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0, &imx_ccm_lock); } diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c index d1f1a19d4351..b2721cae257a 100644 --- a/drivers/clk/mmp/clk-mmp2.c +++ b/drivers/clk/mmp/clk-mmp2.c @@ -248,7 +248,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp2-pwm.3"); clk = clk_register_mux(NULL, "uart0_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART0, 4, 3, 0, &clk_lock); clk_set_parent(clk, vctcxo); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -258,7 +259,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.0"); clk = clk_register_mux(NULL, "uart1_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART1, 4, 3, 0, &clk_lock); clk_set_parent(clk, vctcxo); clk_register_clkdev(clk, "uart_mux.1", NULL); @@ -268,7 +270,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.1"); clk = clk_register_mux(NULL, "uart2_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART2, 4, 3, 0, &clk_lock); clk_set_parent(clk, vctcxo); clk_register_clkdev(clk, "uart_mux.2", NULL); @@ -278,7 +281,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.2"); clk = clk_register_mux(NULL, "uart3_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART3, 4, 3, 0, &clk_lock); clk_set_parent(clk, vctcxo); clk_register_clkdev(clk, "uart_mux.3", NULL); @@ -288,7 +292,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.3"); clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -297,7 +302,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.0"); clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.1", NULL); @@ -306,7 +312,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.1"); clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.2", NULL); @@ -315,7 +322,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.2"); clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.3", NULL); @@ -324,7 +332,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.3"); clk = clk_register_mux(NULL, "sdh_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH0, 8, 2, 0, &clk_lock); clk_register_clkdev(clk, "sdh_mux", NULL); @@ -354,7 +363,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, "usb_clk", NULL); clk = clk_register_mux(NULL, "disp0_mux", disp_parent, - ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(disp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_DISP0, 6, 2, 0, &clk_lock); clk_register_clkdev(clk, "disp_mux.0", NULL); @@ -376,7 +386,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, "disp_sphy.0", NULL); clk = clk_register_mux(NULL, "disp1_mux", disp_parent, - ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(disp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_DISP1, 6, 2, 0, &clk_lock); clk_register_clkdev(clk, "disp_mux.1", NULL); @@ -394,7 +405,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, "ccic_arbiter", NULL); clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent, - ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ccic_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_CCIC0, 6, 2, 0, &clk_lock); clk_register_clkdev(clk, "ccic_mux.0", NULL); @@ -421,7 +433,8 @@ void __init mmp2_clk_init(void) clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0"); clk = clk_register_mux(NULL, "ccic1_mux", ccic_parent, - ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ccic_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_CCIC1, 6, 2, 0, &clk_lock); clk_register_clkdev(clk, "ccic_mux.1", NULL); diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c index 28b3b51c794b..014396b028a2 100644 --- a/drivers/clk/mmp/clk-pxa168.c +++ b/drivers/clk/mmp/clk-pxa168.c @@ -199,7 +199,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa168-pwm.3"); clk = clk_register_mux(NULL, "uart0_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART0, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -209,7 +210,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.0"); clk = clk_register_mux(NULL, "uart1_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART1, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.1", NULL); @@ -219,7 +221,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.1"); clk = clk_register_mux(NULL, "uart2_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART2, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.2", NULL); @@ -229,7 +232,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.2"); clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -238,7 +242,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.0"); clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.1", NULL); @@ -247,7 +252,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.1"); clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.2", NULL); @@ -256,7 +262,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.2"); clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.3", NULL); @@ -265,7 +272,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.3"); clk = clk_register_mux(NULL, "ssp4_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP4, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.4", NULL); @@ -278,7 +286,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "pxa3xx-nand.0"); clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "sdh0_mux", NULL); @@ -287,7 +296,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, NULL, "sdhci-pxa.0"); clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "sdh1_mux", NULL); @@ -304,7 +314,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, "sph_clk", NULL); clk = clk_register_mux(NULL, "disp0_mux", disp_parent, - ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(disp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "disp_mux.0", NULL); @@ -317,7 +328,8 @@ void __init pxa168_clk_init(void) clk_register_clkdev(clk, "hclk", "mmp-disp.0"); clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent, - ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ccic_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "ccic_mux.0", NULL); @@ -327,8 +339,8 @@ void __init pxa168_clk_init(void) clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent, ARRAY_SIZE(ccic_phy_parent), - CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0, - 7, 1, 0, &clk_lock); + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + apmu_base + APMU_CCIC0, 7, 1, 0, &clk_lock); clk_register_clkdev(clk, "ccic_phy_mux.0", NULL); clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux", diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c index 6ec05698ed38..9efc6a47535d 100644 --- a/drivers/clk/mmp/clk-pxa910.c +++ b/drivers/clk/mmp/clk-pxa910.c @@ -204,7 +204,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa910-pwm.3"); clk = clk_register_mux(NULL, "uart0_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART0, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -214,7 +215,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.0"); clk = clk_register_mux(NULL, "uart1_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_UART1, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.1", NULL); @@ -224,7 +226,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.1"); clk = clk_register_mux(NULL, "uart2_mux", uart_parent, - ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbcp_base + APBCP_UART2, 4, 3, 0, &clk_lock); clk_set_parent(clk, uart_pll); clk_register_clkdev(clk, "uart_mux.2", NULL); @@ -234,7 +237,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa2xx-uart.2"); clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "uart_mux.0", NULL); @@ -243,7 +247,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-ssp.0"); clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent, - ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ssp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock); clk_register_clkdev(clk, "ssp_mux.1", NULL); @@ -256,7 +261,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "pxa3xx-nand.0"); clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "sdh0_mux", NULL); @@ -265,7 +271,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "sdhci-pxa.0"); clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent, - ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdh_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "sdh1_mux", NULL); @@ -282,7 +289,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, "sph_clk", NULL); clk = clk_register_mux(NULL, "disp0_mux", disp_parent, - ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(disp_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "disp_mux.0", NULL); @@ -291,7 +299,8 @@ void __init pxa910_clk_init(void) clk_register_clkdev(clk, NULL, "mmp-disp.0"); clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent, - ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + ARRAY_SIZE(ccic_parent), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock); clk_register_clkdev(clk, "ccic_mux.0", NULL); @@ -301,8 +310,8 @@ void __init pxa910_clk_init(void) clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent, ARRAY_SIZE(ccic_phy_parent), - CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0, - 7, 1, 0, &clk_lock); + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + apmu_base + APMU_CCIC0, 7, 1, 0, &clk_lock); clk_register_clkdev(clk, "ccic_phy_mux.0", NULL); clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux", diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h index 81421e28e69c..ef10ad9b5daa 100644 --- a/drivers/clk/mxs/clk.h +++ b/drivers/clk/mxs/clk.h @@ -52,8 +52,8 @@ static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg, u8 shift, u8 width, const char **parent_names, int num_parents) { return clk_register_mux(NULL, name, parent_names, num_parents, - CLK_SET_RATE_PARENT, reg, shift, width, - 0, &mxs_lock); + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + reg, shift, width, 0, &mxs_lock); } static inline struct clk *mxs_clk_fixed_factor(const char *name, diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c index 51b48daf5c8a..39b40aaede2b 100644 --- a/drivers/clk/samsung/clk-exynos-audss.c +++ b/drivers/clk/samsung/clk-exynos-audss.c @@ -82,11 +82,13 @@ static void __init exynos_audss_clk_init(struct device_node *np) of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss", - mout_audss_p, ARRAY_SIZE(mout_audss_p), 0, + mout_audss_p, ARRAY_SIZE(mout_audss_p), + CLK_SET_RATE_NO_REPARENT, reg_base + ASS_CLK_SRC, 0, 1, 0, &lock); clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(NULL, "mout_i2s", - mout_i2s_p, ARRAY_SIZE(mout_i2s_p), 0, + mout_i2s_p, ARRAY_SIZE(mout_i2s_p), + CLK_SET_RATE_NO_REPARENT, reg_base + ASS_CLK_SRC, 2, 2, 0, &lock); clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(NULL, "dout_srp", diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index e7dfccb5d981..31b4174e7a5b 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -130,7 +130,7 @@ struct samsung_mux_clock { .name = cname, \ .parent_names = pnames, \ .num_parents = ARRAY_SIZE(pnames), \ - .flags = f, \ + .flags = (f) | CLK_SET_RATE_NO_REPARENT, \ .offset = o, \ .shift = s, \ .width = w, \ diff --git a/drivers/clk/spear/spear1310_clock.c b/drivers/clk/spear/spear1310_clock.c index aedbbe12f321..65894f7687ed 100644 --- a/drivers/clk/spear/spear1310_clock.c +++ b/drivers/clk/spear/spear1310_clock.c @@ -416,9 +416,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* clock derived from 24 or 25 MHz osc clk */ /* vco-pll */ clk = clk_register_mux(NULL, "vco1_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG, - SPEAR1310_PLL1_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PLL_CFG, SPEAR1310_PLL1_CLK_SHIFT, + SPEAR1310_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco1_mclk", NULL); clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "vco1_mclk", 0, SPEAR1310_PLL1_CTR, SPEAR1310_PLL1_FRQ, pll_rtbl, @@ -427,9 +427,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "pll1_clk", NULL); clk = clk_register_mux(NULL, "vco2_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG, - SPEAR1310_PLL2_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PLL_CFG, SPEAR1310_PLL2_CLK_SHIFT, + SPEAR1310_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco2_mclk", NULL); clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, "vco2_mclk", 0, SPEAR1310_PLL2_CTR, SPEAR1310_PLL2_FRQ, pll_rtbl, @@ -438,9 +438,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "pll2_clk", NULL); clk = clk_register_mux(NULL, "vco3_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1310_PLL_CFG, - SPEAR1310_PLL3_CLK_SHIFT, SPEAR1310_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PLL_CFG, SPEAR1310_PLL3_CLK_SHIFT, + SPEAR1310_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco3_mclk", NULL); clk = clk_register_vco_pll("vco3_clk", "pll3_clk", NULL, "vco3_mclk", 0, SPEAR1310_PLL3_CTR, SPEAR1310_PLL3_FRQ, pll_rtbl, @@ -515,9 +515,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* gpt clocks */ clk = clk_register_mux(NULL, "gpt0_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG, - SPEAR1310_GPT0_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT0_CLK_SHIFT, + SPEAR1310_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt0_mclk", NULL); clk = clk_register_gate(NULL, "gpt0_clk", "gpt0_mclk", 0, SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_GPT0_CLK_ENB, 0, @@ -525,9 +525,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "gpt0"); clk = clk_register_mux(NULL, "gpt1_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG, - SPEAR1310_GPT1_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT1_CLK_SHIFT, + SPEAR1310_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt1_mclk", NULL); clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0, SPEAR1310_PERIP1_CLK_ENB, SPEAR1310_GPT1_CLK_ENB, 0, @@ -535,9 +535,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "gpt1"); clk = clk_register_mux(NULL, "gpt2_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG, - SPEAR1310_GPT2_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT2_CLK_SHIFT, + SPEAR1310_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt2_mclk", NULL); clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0, SPEAR1310_PERIP2_CLK_ENB, SPEAR1310_GPT2_CLK_ENB, 0, @@ -545,9 +545,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "gpt2"); clk = clk_register_mux(NULL, "gpt3_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1310_PERIP_CLK_CFG, - SPEAR1310_GPT3_CLK_SHIFT, SPEAR1310_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GPT3_CLK_SHIFT, + SPEAR1310_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt3_mclk", NULL); clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0, SPEAR1310_PERIP2_CLK_ENB, SPEAR1310_GPT3_CLK_ENB, 0, @@ -562,7 +562,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "uart_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents, - ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart0_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1310_PERIP_CLK_CFG, SPEAR1310_UART_CLK_SHIFT, SPEAR1310_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart0_mclk", NULL); @@ -602,7 +603,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "c3_syn_gclk", NULL); clk = clk_register_mux(NULL, "c3_mclk", c3_parents, - ARRAY_SIZE(c3_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(c3_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1310_PERIP_CLK_CFG, SPEAR1310_C3_CLK_SHIFT, SPEAR1310_C3_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "c3_mclk", NULL); @@ -614,8 +616,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* gmac */ clk = clk_register_mux(NULL, "phy_input_mclk", gmac_phy_input_parents, - ARRAY_SIZE(gmac_phy_input_parents), 0, - SPEAR1310_GMAC_CLK_CFG, + ARRAY_SIZE(gmac_phy_input_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_GMAC_CLK_CFG, SPEAR1310_GMAC_PHY_INPUT_CLK_SHIFT, SPEAR1310_GMAC_PHY_INPUT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "phy_input_mclk", NULL); @@ -627,15 +629,16 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk1, "phy_syn_gclk", NULL); clk = clk_register_mux(NULL, "phy_mclk", gmac_phy_parents, - ARRAY_SIZE(gmac_phy_parents), 0, + ARRAY_SIZE(gmac_phy_parents), CLK_SET_RATE_NO_REPARENT, SPEAR1310_PERIP_CLK_CFG, SPEAR1310_GMAC_PHY_CLK_SHIFT, SPEAR1310_GMAC_PHY_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "stmmacphy.0", NULL); /* clcd */ clk = clk_register_mux(NULL, "clcd_syn_mclk", clcd_synth_parents, - ARRAY_SIZE(clcd_synth_parents), 0, - SPEAR1310_CLCD_CLK_SYNT, SPEAR1310_CLCD_SYNT_CLK_SHIFT, + ARRAY_SIZE(clcd_synth_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_CLCD_CLK_SYNT, + SPEAR1310_CLCD_SYNT_CLK_SHIFT, SPEAR1310_CLCD_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "clcd_syn_mclk", NULL); @@ -645,7 +648,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, "clcd_syn_clk", NULL); clk = clk_register_mux(NULL, "clcd_pixel_mclk", clcd_pixel_parents, - ARRAY_SIZE(clcd_pixel_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(clcd_pixel_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1310_PERIP_CLK_CFG, SPEAR1310_CLCD_CLK_SHIFT, SPEAR1310_CLCD_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "clcd_pixel_mclk", NULL); @@ -657,9 +661,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* i2s */ clk = clk_register_mux(NULL, "i2s_src_mclk", i2s_src_parents, - ARRAY_SIZE(i2s_src_parents), 0, SPEAR1310_I2S_CLK_CFG, - SPEAR1310_I2S_SRC_CLK_SHIFT, SPEAR1310_I2S_SRC_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(i2s_src_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_I2S_CLK_CFG, SPEAR1310_I2S_SRC_CLK_SHIFT, + SPEAR1310_I2S_SRC_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_src_mclk", NULL); clk = clk_register_aux("i2s_prs1_clk", NULL, "i2s_src_mclk", 0, @@ -668,7 +672,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, "i2s_prs1_clk", NULL); clk = clk_register_mux(NULL, "i2s_ref_mclk", i2s_ref_parents, - ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(i2s_ref_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1310_I2S_CLK_CFG, SPEAR1310_I2S_REF_SHIFT, SPEAR1310_I2S_REF_SEL_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_ref_mclk", NULL); @@ -806,13 +811,15 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) /* RAS clks */ clk = clk_register_mux(NULL, "gen_syn0_1_mclk", gen_synth0_1_parents, - ARRAY_SIZE(gen_synth0_1_parents), 0, SPEAR1310_PLL_CFG, + ARRAY_SIZE(gen_synth0_1_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_PLL_CFG, SPEAR1310_RAS_SYNT0_1_CLK_SHIFT, SPEAR1310_RAS_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen_syn0_1_clk", NULL); clk = clk_register_mux(NULL, "gen_syn2_3_mclk", gen_synth2_3_parents, - ARRAY_SIZE(gen_synth2_3_parents), 0, SPEAR1310_PLL_CFG, + ARRAY_SIZE(gen_synth2_3_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_PLL_CFG, SPEAR1310_RAS_SYNT2_3_CLK_SHIFT, SPEAR1310_RAS_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen_syn2_3_clk", NULL); @@ -929,8 +936,8 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk = clk_register_mux(NULL, "smii_rgmii_phy_mclk", smii_rgmii_phy_parents, - ARRAY_SIZE(smii_rgmii_phy_parents), 0, - SPEAR1310_RAS_CTRL_REG1, + ARRAY_SIZE(smii_rgmii_phy_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1310_RAS_CTRL_REG1, SPEAR1310_SMII_RGMII_PHY_CLK_SHIFT, SPEAR1310_PHY_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "stmmacphy.1", NULL); @@ -938,15 +945,15 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, "stmmacphy.4", NULL); clk = clk_register_mux(NULL, "rmii_phy_mclk", rmii_phy_parents, - ARRAY_SIZE(rmii_phy_parents), 0, + ARRAY_SIZE(rmii_phy_parents), CLK_SET_RATE_NO_REPARENT, SPEAR1310_RAS_CTRL_REG1, SPEAR1310_RMII_PHY_CLK_SHIFT, SPEAR1310_PHY_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "stmmacphy.3", NULL); clk = clk_register_mux(NULL, "uart1_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART1_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART1_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart1_mclk", NULL); clk = clk_register_gate(NULL, "uart1_clk", "uart1_mclk", 0, @@ -955,9 +962,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5c800000.serial"); clk = clk_register_mux(NULL, "uart2_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART2_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART2_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart2_mclk", NULL); clk = clk_register_gate(NULL, "uart2_clk", "uart2_mclk", 0, @@ -966,9 +973,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5c900000.serial"); clk = clk_register_mux(NULL, "uart3_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART3_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART3_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart3_mclk", NULL); clk = clk_register_gate(NULL, "uart3_clk", "uart3_mclk", 0, @@ -977,9 +984,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5ca00000.serial"); clk = clk_register_mux(NULL, "uart4_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART4_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART4_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart4_mclk", NULL); clk = clk_register_gate(NULL, "uart4_clk", "uart4_mclk", 0, @@ -988,9 +995,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5cb00000.serial"); clk = clk_register_mux(NULL, "uart5_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_UART5_CLK_SHIFT, SPEAR1310_RAS_UART_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_UART5_CLK_SHIFT, + SPEAR1310_RAS_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart5_mclk", NULL); clk = clk_register_gate(NULL, "uart5_clk", "uart5_mclk", 0, @@ -999,9 +1006,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5cc00000.serial"); clk = clk_register_mux(NULL, "i2c1_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C1_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C1_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c1_mclk", NULL); clk = clk_register_gate(NULL, "i2c1_clk", "i2c1_mclk", 0, @@ -1010,9 +1017,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5cd00000.i2c"); clk = clk_register_mux(NULL, "i2c2_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C2_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C2_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c2_mclk", NULL); clk = clk_register_gate(NULL, "i2c2_clk", "i2c2_mclk", 0, @@ -1021,9 +1028,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5ce00000.i2c"); clk = clk_register_mux(NULL, "i2c3_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C3_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C3_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c3_mclk", NULL); clk = clk_register_gate(NULL, "i2c3_clk", "i2c3_mclk", 0, @@ -1032,9 +1039,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5cf00000.i2c"); clk = clk_register_mux(NULL, "i2c4_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C4_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C4_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c4_mclk", NULL); clk = clk_register_gate(NULL, "i2c4_clk", "i2c4_mclk", 0, @@ -1043,9 +1050,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d000000.i2c"); clk = clk_register_mux(NULL, "i2c5_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C5_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C5_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c5_mclk", NULL); clk = clk_register_gate(NULL, "i2c5_clk", "i2c5_mclk", 0, @@ -1054,9 +1061,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d100000.i2c"); clk = clk_register_mux(NULL, "i2c6_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C6_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C6_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c6_mclk", NULL); clk = clk_register_gate(NULL, "i2c6_clk", "i2c6_mclk", 0, @@ -1065,9 +1072,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d200000.i2c"); clk = clk_register_mux(NULL, "i2c7_mclk", i2c_parents, - ARRAY_SIZE(i2c_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_I2C7_CLK_SHIFT, SPEAR1310_I2C_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(i2c_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_I2C7_CLK_SHIFT, + SPEAR1310_I2C_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2c7_mclk", NULL); clk = clk_register_gate(NULL, "i2c7_clk", "i2c7_mclk", 0, @@ -1076,9 +1083,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d300000.i2c"); clk = clk_register_mux(NULL, "ssp1_mclk", ssp1_parents, - ARRAY_SIZE(ssp1_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_SSP1_CLK_SHIFT, SPEAR1310_SSP1_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(ssp1_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_SSP1_CLK_SHIFT, + SPEAR1310_SSP1_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "ssp1_mclk", NULL); clk = clk_register_gate(NULL, "ssp1_clk", "ssp1_mclk", 0, @@ -1087,9 +1094,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "5d400000.spi"); clk = clk_register_mux(NULL, "pci_mclk", pci_parents, - ARRAY_SIZE(pci_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_PCI_CLK_SHIFT, SPEAR1310_PCI_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(pci_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_PCI_CLK_SHIFT, + SPEAR1310_PCI_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "pci_mclk", NULL); clk = clk_register_gate(NULL, "pci_clk", "pci_mclk", 0, @@ -1098,9 +1105,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "pci"); clk = clk_register_mux(NULL, "tdm1_mclk", tdm_parents, - ARRAY_SIZE(tdm_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_TDM1_CLK_SHIFT, SPEAR1310_TDM_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(tdm_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_TDM1_CLK_SHIFT, + SPEAR1310_TDM_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "tdm1_mclk", NULL); clk = clk_register_gate(NULL, "tdm1_clk", "tdm1_mclk", 0, @@ -1109,9 +1116,9 @@ void __init spear1310_clk_init(void __iomem *misc_base, void __iomem *ras_base) clk_register_clkdev(clk, NULL, "tdm_hdlc.0"); clk = clk_register_mux(NULL, "tdm2_mclk", tdm_parents, - ARRAY_SIZE(tdm_parents), 0, SPEAR1310_RAS_CTRL_REG0, - SPEAR1310_TDM2_CLK_SHIFT, SPEAR1310_TDM_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(tdm_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1310_RAS_CTRL_REG0, SPEAR1310_TDM2_CLK_SHIFT, + SPEAR1310_TDM_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "tdm2_mclk", NULL); clk = clk_register_gate(NULL, "tdm2_clk", "tdm2_mclk", 0, diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c index 9d0b3949db30..fe835c1845fe 100644 --- a/drivers/clk/spear/spear1340_clock.c +++ b/drivers/clk/spear/spear1340_clock.c @@ -473,9 +473,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* clock derived from 24 or 25 MHz osc clk */ /* vco-pll */ clk = clk_register_mux(NULL, "vco1_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG, - SPEAR1340_PLL1_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PLL_CFG, SPEAR1340_PLL1_CLK_SHIFT, + SPEAR1340_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco1_mclk", NULL); clk = clk_register_vco_pll("vco1_clk", "pll1_clk", NULL, "vco1_mclk", 0, SPEAR1340_PLL1_CTR, SPEAR1340_PLL1_FRQ, pll_rtbl, @@ -484,9 +484,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "pll1_clk", NULL); clk = clk_register_mux(NULL, "vco2_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG, - SPEAR1340_PLL2_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PLL_CFG, SPEAR1340_PLL2_CLK_SHIFT, + SPEAR1340_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco2_mclk", NULL); clk = clk_register_vco_pll("vco2_clk", "pll2_clk", NULL, "vco2_mclk", 0, SPEAR1340_PLL2_CTR, SPEAR1340_PLL2_FRQ, pll_rtbl, @@ -495,9 +495,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "pll2_clk", NULL); clk = clk_register_mux(NULL, "vco3_mclk", vco_parents, - ARRAY_SIZE(vco_parents), 0, SPEAR1340_PLL_CFG, - SPEAR1340_PLL3_CLK_SHIFT, SPEAR1340_PLL_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(vco_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PLL_CFG, SPEAR1340_PLL3_CLK_SHIFT, + SPEAR1340_PLL_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "vco3_mclk", NULL); clk = clk_register_vco_pll("vco3_clk", "pll3_clk", NULL, "vco3_mclk", 0, SPEAR1340_PLL3_CTR, SPEAR1340_PLL3_FRQ, pll_rtbl, @@ -561,8 +561,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "amba_syn_clk", NULL); clk = clk_register_mux(NULL, "sys_mclk", sys_parents, - ARRAY_SIZE(sys_parents), 0, SPEAR1340_SYS_CLK_CTRL, - SPEAR1340_SCLK_SRC_SEL_SHIFT, + ARRAY_SIZE(sys_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_SYS_CLK_CTRL, SPEAR1340_SCLK_SRC_SEL_SHIFT, SPEAR1340_SCLK_SRC_SEL_MASK, 0, &_lock); clk_register_clkdev(clk, "sys_mclk", NULL); @@ -583,8 +583,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "smp_twd"); clk = clk_register_mux(NULL, "ahb_clk", ahb_parents, - ARRAY_SIZE(ahb_parents), 0, SPEAR1340_SYS_CLK_CTRL, - SPEAR1340_HCLK_SRC_SEL_SHIFT, + ARRAY_SIZE(ahb_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_SYS_CLK_CTRL, SPEAR1340_HCLK_SRC_SEL_SHIFT, SPEAR1340_HCLK_SRC_SEL_MASK, 0, &_lock); clk_register_clkdev(clk, "ahb_clk", NULL); @@ -594,9 +594,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* gpt clocks */ clk = clk_register_mux(NULL, "gpt0_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_GPT0_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT0_CLK_SHIFT, + SPEAR1340_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt0_mclk", NULL); clk = clk_register_gate(NULL, "gpt0_clk", "gpt0_mclk", 0, SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_GPT0_CLK_ENB, 0, @@ -604,9 +604,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "gpt0"); clk = clk_register_mux(NULL, "gpt1_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_GPT1_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT1_CLK_SHIFT, + SPEAR1340_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt1_mclk", NULL); clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0, SPEAR1340_PERIP1_CLK_ENB, SPEAR1340_GPT1_CLK_ENB, 0, @@ -614,9 +614,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "gpt1"); clk = clk_register_mux(NULL, "gpt2_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_GPT2_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT2_CLK_SHIFT, + SPEAR1340_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt2_mclk", NULL); clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0, SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_GPT2_CLK_ENB, 0, @@ -624,9 +624,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "gpt2"); clk = clk_register_mux(NULL, "gpt3_mclk", gpt_parents, - ARRAY_SIZE(gpt_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_GPT3_CLK_SHIFT, SPEAR1340_GPT_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gpt_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GPT3_CLK_SHIFT, + SPEAR1340_GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt3_mclk", NULL); clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0, SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_GPT3_CLK_ENB, 0, @@ -641,7 +641,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "uart0_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents, - ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart0_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_UART0_CLK_SHIFT, SPEAR1340_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart0_mclk", NULL); @@ -658,9 +659,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "uart1_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart1_mclk", uart1_parents, - ARRAY_SIZE(uart1_parents), 0, SPEAR1340_PERIP_CLK_CFG, - SPEAR1340_UART1_CLK_SHIFT, SPEAR1340_UART_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(uart1_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_PERIP_CLK_CFG, SPEAR1340_UART1_CLK_SHIFT, + SPEAR1340_UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart1_mclk", NULL); clk = clk_register_gate(NULL, "uart1_clk", "uart1_mclk", 0, @@ -698,7 +699,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "c3_syn_gclk", NULL); clk = clk_register_mux(NULL, "c3_mclk", c3_parents, - ARRAY_SIZE(c3_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(c3_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_C3_CLK_SHIFT, SPEAR1340_C3_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "c3_mclk", NULL); @@ -710,8 +712,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* gmac */ clk = clk_register_mux(NULL, "phy_input_mclk", gmac_phy_input_parents, - ARRAY_SIZE(gmac_phy_input_parents), 0, - SPEAR1340_GMAC_CLK_CFG, + ARRAY_SIZE(gmac_phy_input_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1340_GMAC_CLK_CFG, SPEAR1340_GMAC_PHY_INPUT_CLK_SHIFT, SPEAR1340_GMAC_PHY_INPUT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "phy_input_mclk", NULL); @@ -723,15 +725,16 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "phy_syn_gclk", NULL); clk = clk_register_mux(NULL, "phy_mclk", gmac_phy_parents, - ARRAY_SIZE(gmac_phy_parents), 0, + ARRAY_SIZE(gmac_phy_parents), CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_GMAC_PHY_CLK_SHIFT, SPEAR1340_GMAC_PHY_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "stmmacphy.0", NULL); /* clcd */ clk = clk_register_mux(NULL, "clcd_syn_mclk", clcd_synth_parents, - ARRAY_SIZE(clcd_synth_parents), 0, - SPEAR1340_CLCD_CLK_SYNT, SPEAR1340_CLCD_SYNT_CLK_SHIFT, + ARRAY_SIZE(clcd_synth_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1340_CLCD_CLK_SYNT, + SPEAR1340_CLCD_SYNT_CLK_SHIFT, SPEAR1340_CLCD_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "clcd_syn_mclk", NULL); @@ -741,7 +744,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "clcd_syn_clk", NULL); clk = clk_register_mux(NULL, "clcd_pixel_mclk", clcd_pixel_parents, - ARRAY_SIZE(clcd_pixel_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(clcd_pixel_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_CLCD_CLK_SHIFT, SPEAR1340_CLCD_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "clcd_pixel_mclk", NULL); @@ -753,9 +757,9 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* i2s */ clk = clk_register_mux(NULL, "i2s_src_mclk", i2s_src_parents, - ARRAY_SIZE(i2s_src_parents), 0, SPEAR1340_I2S_CLK_CFG, - SPEAR1340_I2S_SRC_CLK_SHIFT, SPEAR1340_I2S_SRC_CLK_MASK, - 0, &_lock); + ARRAY_SIZE(i2s_src_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR1340_I2S_CLK_CFG, SPEAR1340_I2S_SRC_CLK_SHIFT, + SPEAR1340_I2S_SRC_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_src_mclk", NULL); clk = clk_register_aux("i2s_prs1_clk", NULL, "i2s_src_mclk", @@ -765,7 +769,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "i2s_prs1_clk", NULL); clk = clk_register_mux(NULL, "i2s_ref_mclk", i2s_ref_parents, - ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(i2s_ref_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_I2S_CLK_CFG, SPEAR1340_I2S_REF_SHIFT, SPEAR1340_I2S_REF_SEL_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_ref_mclk", NULL); @@ -891,13 +896,15 @@ void __init spear1340_clk_init(void __iomem *misc_base) /* RAS clks */ clk = clk_register_mux(NULL, "gen_syn0_1_mclk", gen_synth0_1_parents, - ARRAY_SIZE(gen_synth0_1_parents), 0, SPEAR1340_PLL_CFG, + ARRAY_SIZE(gen_synth0_1_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1340_PLL_CFG, SPEAR1340_GEN_SYNT0_1_CLK_SHIFT, SPEAR1340_GEN_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen_syn0_1_mclk", NULL); clk = clk_register_mux(NULL, "gen_syn2_3_mclk", gen_synth2_3_parents, - ARRAY_SIZE(gen_synth2_3_parents), 0, SPEAR1340_PLL_CFG, + ARRAY_SIZE(gen_synth2_3_parents), + CLK_SET_RATE_NO_REPARENT, SPEAR1340_PLL_CFG, SPEAR1340_GEN_SYNT2_3_CLK_SHIFT, SPEAR1340_GEN_SYNT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen_syn2_3_mclk", NULL); @@ -938,7 +945,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "spear_cec.1"); clk = clk_register_mux(NULL, "spdif_out_mclk", spdif_out_parents, - ARRAY_SIZE(spdif_out_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(spdif_out_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_SPDIF_OUT_CLK_SHIFT, SPEAR1340_SPDIF_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "spdif_out_mclk", NULL); @@ -949,7 +957,8 @@ void __init spear1340_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, NULL, "d0000000.spdif-out"); clk = clk_register_mux(NULL, "spdif_in_mclk", spdif_in_parents, - ARRAY_SIZE(spdif_in_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(spdif_in_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR1340_PERIP_CLK_CFG, SPEAR1340_SPDIF_IN_CLK_SHIFT, SPEAR1340_SPDIF_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "spdif_in_mclk", NULL); diff --git a/drivers/clk/spear/spear3xx_clock.c b/drivers/clk/spear/spear3xx_clock.c index 080c3c5e33f6..c2d204315546 100644 --- a/drivers/clk/spear/spear3xx_clock.c +++ b/drivers/clk/spear/spear3xx_clock.c @@ -294,7 +294,8 @@ static void __init spear320_clk_init(void __iomem *soc_config_base) clk_register_clkdev(clk, NULL, "a9400000.i2s"); clk = clk_register_mux(NULL, "i2s_ref_clk", i2s_ref_parents, - ARRAY_SIZE(i2s_ref_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(i2s_ref_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_CONTROL_REG, I2S_REF_PCLK_SHIFT, I2S_REF_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, "i2s_ref_clk", NULL); @@ -313,57 +314,66 @@ static void __init spear320_clk_init(void __iomem *soc_config_base) clk_register_clkdev(clk, "hclk", "ab000000.eth"); clk = clk_register_mux(NULL, "rs485_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_RS485_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a9300000.serial"); clk = clk_register_mux(NULL, "sdhci_clk", sdhci_parents, - ARRAY_SIZE(sdhci_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(sdhci_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_CONTROL_REG, SDHCI_PCLK_SHIFT, SDHCI_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "70000000.sdhci"); clk = clk_register_mux(NULL, "smii_pclk", smii0_parents, - ARRAY_SIZE(smii0_parents), 0, SPEAR320_CONTROL_REG, - SMII_PCLK_SHIFT, SMII_PCLK_MASK, 0, &_lock); + ARRAY_SIZE(smii0_parents), CLK_SET_RATE_NO_REPARENT, + SPEAR320_CONTROL_REG, SMII_PCLK_SHIFT, SMII_PCLK_MASK, + 0, &_lock); clk_register_clkdev(clk, NULL, "smii_pclk"); clk = clk_register_fixed_factor(NULL, "smii_clk", "smii_pclk", 0, 1, 1); clk_register_clkdev(clk, NULL, "smii"); clk = clk_register_mux(NULL, "uart1_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_CONTROL_REG, UART1_PCLK_SHIFT, UART1_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a3000000.serial"); clk = clk_register_mux(NULL, "uart2_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART2_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a4000000.serial"); clk = clk_register_mux(NULL, "uart3_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART3_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a9100000.serial"); clk = clk_register_mux(NULL, "uart4_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART4_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "a9200000.serial"); clk = clk_register_mux(NULL, "uart5_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART5_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "60000000.serial"); clk = clk_register_mux(NULL, "uart6_clk", uartx_parents, - ARRAY_SIZE(uartx_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uartx_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, SPEAR320_EXT_CTRL_REG, SPEAR320_UART6_PCLK_SHIFT, SPEAR320_UARTX_PCLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "60100000.serial"); @@ -427,7 +437,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_clkdev(clk1, "uart_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart0_mclk", uart0_parents, - ARRAY_SIZE(uart0_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(uart0_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "uart0_mclk", NULL); @@ -444,7 +455,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_clkdev(clk1, "firda_syn_gclk", NULL); clk = clk_register_mux(NULL, "firda_mclk", firda_parents, - ARRAY_SIZE(firda_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(firda_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "firda_mclk", NULL); @@ -458,14 +470,16 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_gpt("gpt0_syn_clk", "pll1_clk", 0, PRSC0_CLK_CFG, gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); clk = clk_register_mux(NULL, "gpt0_clk", gpt0_parents, - ARRAY_SIZE(gpt0_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(gpt0_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "gpt0"); clk_register_gpt("gpt1_syn_clk", "pll1_clk", 0, PRSC1_CLK_CFG, gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); clk = clk_register_mux(NULL, "gpt1_mclk", gpt1_parents, - ARRAY_SIZE(gpt1_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(gpt1_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt1_mclk", NULL); clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", @@ -476,7 +490,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_gpt("gpt2_syn_clk", "pll1_clk", 0, PRSC2_CLK_CFG, gpt_rtbl, ARRAY_SIZE(gpt_rtbl), &_lock); clk = clk_register_mux(NULL, "gpt2_mclk", gpt2_parents, - ARRAY_SIZE(gpt2_parents), CLK_SET_RATE_PARENT, + ARRAY_SIZE(gpt2_parents), + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, PERIP_CLK_CFG, GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt2_mclk", NULL); clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", @@ -498,9 +513,9 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_clkdev(clk1, "gen1_syn_gclk", NULL); clk = clk_register_mux(NULL, "gen2_3_par_clk", gen2_3_parents, - ARRAY_SIZE(gen2_3_parents), 0, CORE_CLK_CFG, - GEN_SYNTH2_3_CLK_SHIFT, GEN_SYNTH2_3_CLK_MASK, 0, - &_lock); + ARRAY_SIZE(gen2_3_parents), CLK_SET_RATE_NO_REPARENT, + CORE_CLK_CFG, GEN_SYNTH2_3_CLK_SHIFT, + GEN_SYNTH2_3_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gen2_3_par_clk", NULL); clk = clk_register_aux("gen2_syn_clk", "gen2_syn_gclk", @@ -540,8 +555,8 @@ void __init spear3xx_clk_init(void __iomem *misc_base, void __iomem *soc_config_ clk_register_clkdev(clk, "ahbmult2_clk", NULL); clk = clk_register_mux(NULL, "ddr_clk", ddr_parents, - ARRAY_SIZE(ddr_parents), 0, PLL_CLK_CFG, MCTR_CLK_SHIFT, - MCTR_CLK_MASK, 0, &_lock); + ARRAY_SIZE(ddr_parents), CLK_SET_RATE_NO_REPARENT, + PLL_CLK_CFG, MCTR_CLK_SHIFT, MCTR_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "ddr_clk", NULL); clk = clk_register_divider(NULL, "apb_clk", "ahb_clk", diff --git a/drivers/clk/spear/spear6xx_clock.c b/drivers/clk/spear/spear6xx_clock.c index 9406f2426d64..4f649c9cb094 100644 --- a/drivers/clk/spear/spear6xx_clock.c +++ b/drivers/clk/spear/spear6xx_clock.c @@ -169,8 +169,9 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "uart_syn_gclk", NULL); clk = clk_register_mux(NULL, "uart_mclk", uart_parents, - ARRAY_SIZE(uart_parents), 0, PERIP_CLK_CFG, - UART_CLK_SHIFT, UART_CLK_MASK, 0, &_lock); + ARRAY_SIZE(uart_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, UART_CLK_SHIFT, UART_CLK_MASK, 0, + &_lock); clk_register_clkdev(clk, "uart_mclk", NULL); clk = clk_register_gate(NULL, "uart0", "uart_mclk", 0, PERIP1_CLK_ENB, @@ -188,8 +189,9 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "firda_syn_gclk", NULL); clk = clk_register_mux(NULL, "firda_mclk", firda_parents, - ARRAY_SIZE(firda_parents), 0, PERIP_CLK_CFG, - FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, &_lock); + ARRAY_SIZE(firda_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, FIRDA_CLK_SHIFT, FIRDA_CLK_MASK, 0, + &_lock); clk_register_clkdev(clk, "firda_mclk", NULL); clk = clk_register_gate(NULL, "firda_clk", "firda_mclk", 0, @@ -203,8 +205,9 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk1, "clcd_syn_gclk", NULL); clk = clk_register_mux(NULL, "clcd_mclk", clcd_parents, - ARRAY_SIZE(clcd_parents), 0, PERIP_CLK_CFG, - CLCD_CLK_SHIFT, CLCD_CLK_MASK, 0, &_lock); + ARRAY_SIZE(clcd_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, CLCD_CLK_SHIFT, CLCD_CLK_MASK, 0, + &_lock); clk_register_clkdev(clk, "clcd_mclk", NULL); clk = clk_register_gate(NULL, "clcd_clk", "clcd_mclk", 0, @@ -217,13 +220,13 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "gpt0_1_syn_clk", NULL); clk = clk_register_mux(NULL, "gpt0_mclk", gpt0_1_parents, - ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG, - GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + ARRAY_SIZE(gpt0_1_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, GPT0_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, NULL, "gpt0"); clk = clk_register_mux(NULL, "gpt1_mclk", gpt0_1_parents, - ARRAY_SIZE(gpt0_1_parents), 0, PERIP_CLK_CFG, - GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + ARRAY_SIZE(gpt0_1_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, GPT1_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt1_mclk", NULL); clk = clk_register_gate(NULL, "gpt1_clk", "gpt1_mclk", 0, @@ -235,8 +238,8 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "gpt2_syn_clk", NULL); clk = clk_register_mux(NULL, "gpt2_mclk", gpt2_parents, - ARRAY_SIZE(gpt2_parents), 0, PERIP_CLK_CFG, - GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + ARRAY_SIZE(gpt2_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, GPT2_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt2_mclk", NULL); clk = clk_register_gate(NULL, "gpt2_clk", "gpt2_mclk", 0, @@ -248,8 +251,8 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "gpt3_syn_clk", NULL); clk = clk_register_mux(NULL, "gpt3_mclk", gpt3_parents, - ARRAY_SIZE(gpt3_parents), 0, PERIP_CLK_CFG, - GPT3_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); + ARRAY_SIZE(gpt3_parents), CLK_SET_RATE_NO_REPARENT, + PERIP_CLK_CFG, GPT3_CLK_SHIFT, GPT_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "gpt3_mclk", NULL); clk = clk_register_gate(NULL, "gpt3_clk", "gpt3_mclk", 0, @@ -277,8 +280,8 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk_register_clkdev(clk, "ahbmult2_clk", NULL); clk = clk_register_mux(NULL, "ddr_clk", ddr_parents, - ARRAY_SIZE(ddr_parents), 0, PLL_CLK_CFG, MCTR_CLK_SHIFT, - MCTR_CLK_MASK, 0, &_lock); + ARRAY_SIZE(ddr_parents), CLK_SET_RATE_NO_REPARENT, + PLL_CLK_CFG, MCTR_CLK_SHIFT, MCTR_CLK_MASK, 0, &_lock); clk_register_clkdev(clk, "ddr_clk", NULL); clk = clk_register_divider(NULL, "apb_clk", "ahb_clk", diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 02e440beb3d3..323830465d13 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -261,7 +261,8 @@ static void __init sunxi_mux_clk_setup(struct device_node *node, while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL) i++; - clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg, + clk = clk_register_mux(NULL, clk_name, parents, i, + CLK_SET_RATE_NO_REPARENT, reg, data->shift, SUNXI_MUX_GATE_WIDTH, 0, &clk_lock); diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index 2d3b3dc5c1ec..be40856a6fa4 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -1558,7 +1558,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base) /* audio0 */ clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0, NULL); clks[audio0_mux] = clk; @@ -1570,7 +1571,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base) /* audio1 */ clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0, NULL); clks[audio1_mux] = clk; @@ -1582,7 +1584,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base) /* audio2 */ clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0, NULL); clks[audio2_mux] = clk; @@ -1594,7 +1597,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base) /* audio3 */ clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0, NULL); clks[audio3_mux] = clk; @@ -1606,7 +1610,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base) /* audio4 */ clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0, NULL); clks[audio4_mux] = clk; @@ -1618,7 +1623,8 @@ static void __init tegra114_audio_clk_init(void __iomem *clk_base) /* spdif */ clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0, NULL); clks[spdif_mux] = clk; @@ -1713,7 +1719,8 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) /* clk_out_1 */ clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out1_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0, &clk_out_lock); clks[clk_out_1_mux] = clk; @@ -1725,7 +1732,8 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) /* clk_out_2 */ clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents, - ARRAY_SIZE(clk_out2_parents), 0, + ARRAY_SIZE(clk_out2_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0, &clk_out_lock); clks[clk_out_2_mux] = clk; @@ -1737,7 +1745,8 @@ static void __init tegra114_pmc_clk_init(void __iomem *pmc_base) /* clk_out_3 */ clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents, - ARRAY_SIZE(clk_out3_parents), 0, + ARRAY_SIZE(clk_out3_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0, &clk_out_lock); clks[clk_out_3_mux] = clk; @@ -2055,7 +2064,8 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base) /* dsia */ clk = clk_register_mux(NULL, "dsia_mux", mux_plld_out0_plld2_out0, - ARRAY_SIZE(mux_plld_out0_plld2_out0), 0, + ARRAY_SIZE(mux_plld_out0_plld2_out0), + CLK_SET_RATE_NO_REPARENT, clk_base + PLLD_BASE, 25, 1, 0, &pll_d_lock); clks[dsia_mux] = clk; clk = tegra_clk_register_periph_gate("dsia", "dsia_mux", 0, clk_base, @@ -2065,7 +2075,8 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base) /* dsib */ clk = clk_register_mux(NULL, "dsib_mux", mux_plld_out0_plld2_out0, - ARRAY_SIZE(mux_plld_out0_plld2_out0), 0, + ARRAY_SIZE(mux_plld_out0_plld2_out0), + CLK_SET_RATE_NO_REPARENT, clk_base + PLLD2_BASE, 25, 1, 0, &pll_d2_lock); clks[dsib_mux] = clk; clk = tegra_clk_register_periph_gate("dsib", "dsib_mux", 0, clk_base, @@ -2102,7 +2113,8 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base) /* emc */ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), 0, + ARRAY_SIZE(mux_pllmcp_clkm), + CLK_SET_RATE_NO_REPARENT, clk_base + CLK_SOURCE_EMC, 29, 3, 0, NULL); clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index ebe94ce0f647..056f649d0d89 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -778,7 +778,8 @@ static void __init tegra20_audio_clk_init(void) /* audio */ clk = clk_register_mux(NULL, "audio_mux", audio_parents, - ARRAY_SIZE(audio_parents), 0, + ARRAY_SIZE(audio_parents), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio", "audio_mux", 0, clk_base + AUDIO_SYNC_CLK, 4, @@ -941,7 +942,8 @@ static void __init tegra20_periph_clk_init(void) /* emc */ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), 0, + ARRAY_SIZE(mux_pllmcp_clkm), + CLK_SET_RATE_NO_REPARENT, clk_base + CLK_SOURCE_EMC, 30, 2, 0, NULL); clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index ab35040231cd..b09ebf64cdc8 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -1026,7 +1026,8 @@ static void __init tegra30_pll_init(void) /* PLLE */ clk = clk_register_mux(NULL, "pll_e_mux", pll_e_parents, - ARRAY_SIZE(pll_e_parents), 0, + ARRAY_SIZE(pll_e_parents), + CLK_SET_RATE_NO_REPARENT, clk_base + PLLE_AUX, 2, 1, 0, NULL); clk = tegra_clk_register_plle("pll_e", "pll_e_mux", clk_base, pmc_base, CLK_GET_RATE_NOCACHE, 100000000, &pll_e_params, @@ -1086,7 +1087,8 @@ static void __init tegra30_audio_clk_init(void) /* audio0 */ clk = clk_register_mux(NULL, "audio0_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S0, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio0", "audio0_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S0, 4, @@ -1096,7 +1098,8 @@ static void __init tegra30_audio_clk_init(void) /* audio1 */ clk = clk_register_mux(NULL, "audio1_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S1, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio1", "audio1_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S1, 4, @@ -1106,7 +1109,8 @@ static void __init tegra30_audio_clk_init(void) /* audio2 */ clk = clk_register_mux(NULL, "audio2_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S2, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio2", "audio2_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S2, 4, @@ -1116,7 +1120,8 @@ static void __init tegra30_audio_clk_init(void) /* audio3 */ clk = clk_register_mux(NULL, "audio3_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S3, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio3", "audio3_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S3, 4, @@ -1126,7 +1131,8 @@ static void __init tegra30_audio_clk_init(void) /* audio4 */ clk = clk_register_mux(NULL, "audio4_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_I2S4, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "audio4", "audio4_mux", 0, clk_base + AUDIO_SYNC_CLK_I2S4, 4, @@ -1136,7 +1142,8 @@ static void __init tegra30_audio_clk_init(void) /* spdif */ clk = clk_register_mux(NULL, "spdif_mux", mux_audio_sync_clk, - ARRAY_SIZE(mux_audio_sync_clk), 0, + ARRAY_SIZE(mux_audio_sync_clk), + CLK_SET_RATE_NO_REPARENT, clk_base + AUDIO_SYNC_CLK_SPDIF, 0, 3, 0, NULL); clk = clk_register_gate(NULL, "spdif", "spdif_mux", 0, clk_base + AUDIO_SYNC_CLK_SPDIF, 4, @@ -1229,7 +1236,8 @@ static void __init tegra30_pmc_clk_init(void) /* clk_out_1 */ clk = clk_register_mux(NULL, "clk_out_1_mux", clk_out1_parents, - ARRAY_SIZE(clk_out1_parents), 0, + ARRAY_SIZE(clk_out1_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 6, 3, 0, &clk_out_lock); clks[clk_out_1_mux] = clk; @@ -1241,7 +1249,8 @@ static void __init tegra30_pmc_clk_init(void) /* clk_out_2 */ clk = clk_register_mux(NULL, "clk_out_2_mux", clk_out2_parents, - ARRAY_SIZE(clk_out2_parents), 0, + ARRAY_SIZE(clk_out2_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 14, 3, 0, &clk_out_lock); clk = clk_register_gate(NULL, "clk_out_2", "clk_out_2_mux", 0, @@ -1252,7 +1261,8 @@ static void __init tegra30_pmc_clk_init(void) /* clk_out_3 */ clk = clk_register_mux(NULL, "clk_out_3_mux", clk_out3_parents, - ARRAY_SIZE(clk_out3_parents), 0, + ARRAY_SIZE(clk_out3_parents), + CLK_SET_RATE_NO_REPARENT, pmc_base + PMC_CLK_OUT_CNTRL, 22, 3, 0, &clk_out_lock); clk = clk_register_gate(NULL, "clk_out_3", "clk_out_3_mux", 0, @@ -1679,7 +1689,8 @@ static void __init tegra30_periph_clk_init(void) /* emc */ clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), 0, + ARRAY_SIZE(mux_pllmcp_clkm), + CLK_SET_RATE_NO_REPARENT, clk_base + CLK_SOURCE_EMC, 30, 2, 0, NULL); clk = tegra_clk_register_periph_gate("emc", "emc_mux", 0, clk_base, 0, diff --git a/drivers/clk/versatile/clk-vexpress.c b/drivers/clk/versatile/clk-vexpress.c index a4a728d05092..2d5e1b4820e0 100644 --- a/drivers/clk/versatile/clk-vexpress.c +++ b/drivers/clk/versatile/clk-vexpress.c @@ -37,8 +37,8 @@ static void __init vexpress_sp810_init(void __iomem *base) snprintf(name, ARRAY_SIZE(name), "timerclken%d", i); vexpress_sp810_timerclken[i] = clk_register_mux(NULL, name, - parents, 2, 0, base + SCCTRL, - SCCTRL_TIMERENnSEL_SHIFT(i), 1, + parents, 2, CLK_SET_RATE_NO_REPARENT, + base + SCCTRL, SCCTRL_TIMERENnSEL_SHIFT(i), 1, 0, &vexpress_sp810_lock); if (WARN_ON(IS_ERR(vexpress_sp810_timerclken[i]))) diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 5c205b60a82a..e05c9e3f1385 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -124,8 +124,9 @@ static void __init zynq_clk_register_fclk(enum zynq_clk fclk, div0_name = kasprintf(GFP_KERNEL, "%s_div0", clk_name); div1_name = kasprintf(GFP_KERNEL, "%s_div1", clk_name); - clk = clk_register_mux(NULL, mux_name, parents, 4, 0, - fclk_ctrl_reg, 4, 2, 0, fclk_lock); + clk = clk_register_mux(NULL, mux_name, parents, 4, + CLK_SET_RATE_NO_REPARENT, fclk_ctrl_reg, 4, 2, 0, + fclk_lock); clk = clk_register_divider(NULL, div0_name, mux_name, 0, fclk_ctrl_reg, 8, 6, CLK_DIVIDER_ONE_BASED | @@ -167,8 +168,8 @@ static void __init zynq_clk_register_periph_clk(enum zynq_clk clk0, mux_name = kasprintf(GFP_KERNEL, "%s_mux", clk_name0); div_name = kasprintf(GFP_KERNEL, "%s_div", clk_name0); - clk = clk_register_mux(NULL, mux_name, parents, 4, 0, - clk_ctrl, 4, 2, 0, lock); + clk = clk_register_mux(NULL, mux_name, parents, 4, + CLK_SET_RATE_NO_REPARENT, clk_ctrl, 4, 2, 0, lock); clk = clk_register_divider(NULL, div_name, mux_name, 0, clk_ctrl, 8, 6, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, lock); @@ -235,25 +236,26 @@ static void __init zynq_clk_setup(struct device_node *np) clk = clk_register_zynq_pll("armpll_int", "ps_clk", SLCR_ARMPLL_CTRL, SLCR_PLL_STATUS, 0, &armpll_lock); clks[armpll] = clk_register_mux(NULL, clk_output_name[armpll], - armpll_parents, 2, 0, SLCR_ARMPLL_CTRL, 4, 1, 0, - &armpll_lock); + armpll_parents, 2, CLK_SET_RATE_NO_REPARENT, + SLCR_ARMPLL_CTRL, 4, 1, 0, &armpll_lock); clk = clk_register_zynq_pll("ddrpll_int", "ps_clk", SLCR_DDRPLL_CTRL, SLCR_PLL_STATUS, 1, &ddrpll_lock); clks[ddrpll] = clk_register_mux(NULL, clk_output_name[ddrpll], - ddrpll_parents, 2, 0, SLCR_DDRPLL_CTRL, 4, 1, 0, - &ddrpll_lock); + ddrpll_parents, 2, CLK_SET_RATE_NO_REPARENT, + SLCR_DDRPLL_CTRL, 4, 1, 0, &ddrpll_lock); clk = clk_register_zynq_pll("iopll_int", "ps_clk", SLCR_IOPLL_CTRL, SLCR_PLL_STATUS, 2, &iopll_lock); clks[iopll] = clk_register_mux(NULL, clk_output_name[iopll], - iopll_parents, 2, 0, SLCR_IOPLL_CTRL, 4, 1, 0, - &iopll_lock); + iopll_parents, 2, CLK_SET_RATE_NO_REPARENT, + SLCR_IOPLL_CTRL, 4, 1, 0, &iopll_lock); /* CPU clocks */ tmp = readl(SLCR_621_TRUE) & 1; - clk = clk_register_mux(NULL, "cpu_mux", cpu_parents, 4, 0, - SLCR_ARM_CLK_CTRL, 4, 2, 0, &armclk_lock); + clk = clk_register_mux(NULL, "cpu_mux", cpu_parents, 4, + CLK_SET_RATE_NO_REPARENT, SLCR_ARM_CLK_CTRL, 4, 2, 0, + &armclk_lock); clk = clk_register_divider(NULL, "cpu_div", "cpu_mux", 0, SLCR_ARM_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, &armclk_lock); @@ -292,8 +294,9 @@ static void __init zynq_clk_setup(struct device_node *np) swdt_ext_clk_mux_parents[i + 1] = dummy_nm; } clks[swdt] = clk_register_mux(NULL, clk_output_name[swdt], - swdt_ext_clk_mux_parents, 2, CLK_SET_RATE_PARENT, - SLCR_SWDT_CLK_SEL, 0, 1, 0, &gem0clk_lock); + swdt_ext_clk_mux_parents, 2, CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT, SLCR_SWDT_CLK_SEL, 0, 1, 0, + &gem0clk_lock); /* DDR clocks */ clk = clk_register_divider(NULL, "ddr2x_div", "ddrpll", 0, @@ -355,8 +358,9 @@ static void __init zynq_clk_setup(struct device_node *np) gem0_mux_parents[i + 1] = of_clk_get_parent_name(np, idx); } - clk = clk_register_mux(NULL, "gem0_mux", periph_parents, 4, 0, - SLCR_GEM0_CLK_CTRL, 4, 2, 0, &gem0clk_lock); + clk = clk_register_mux(NULL, "gem0_mux", periph_parents, 4, + CLK_SET_RATE_NO_REPARENT, SLCR_GEM0_CLK_CTRL, 4, 2, 0, + &gem0clk_lock); clk = clk_register_divider(NULL, "gem0_div0", "gem0_mux", 0, SLCR_GEM0_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, &gem0clk_lock); @@ -364,8 +368,9 @@ static void __init zynq_clk_setup(struct device_node *np) CLK_SET_RATE_PARENT, SLCR_GEM0_CLK_CTRL, 20, 6, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, &gem0clk_lock); - clk = clk_register_mux(NULL, "gem0_emio_mux", gem0_mux_parents, 2, 0, - SLCR_GEM0_CLK_CTRL, 6, 1, 0, &gem0clk_lock); + clk = clk_register_mux(NULL, "gem0_emio_mux", gem0_mux_parents, 2, + CLK_SET_RATE_NO_REPARENT, SLCR_GEM0_CLK_CTRL, 6, 1, 0, + &gem0clk_lock); clks[gem0] = clk_register_gate(NULL, clk_output_name[gem0], "gem0_emio_mux", CLK_SET_RATE_PARENT, SLCR_GEM0_CLK_CTRL, 0, 0, &gem0clk_lock); @@ -377,8 +382,9 @@ static void __init zynq_clk_setup(struct device_node *np) gem1_mux_parents[i + 1] = of_clk_get_parent_name(np, idx); } - clk = clk_register_mux(NULL, "gem1_mux", periph_parents, 4, 0, - SLCR_GEM1_CLK_CTRL, 4, 2, 0, &gem1clk_lock); + clk = clk_register_mux(NULL, "gem1_mux", periph_parents, 4, + CLK_SET_RATE_NO_REPARENT, SLCR_GEM1_CLK_CTRL, 4, 2, 0, + &gem1clk_lock); clk = clk_register_divider(NULL, "gem1_div0", "gem1_mux", 0, SLCR_GEM1_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, &gem1clk_lock); @@ -386,8 +392,9 @@ static void __init zynq_clk_setup(struct device_node *np) CLK_SET_RATE_PARENT, SLCR_GEM1_CLK_CTRL, 20, 6, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, &gem1clk_lock); - clk = clk_register_mux(NULL, "gem1_emio_mux", gem1_mux_parents, 2, 0, - SLCR_GEM1_CLK_CTRL, 6, 1, 0, &gem1clk_lock); + clk = clk_register_mux(NULL, "gem1_emio_mux", gem1_mux_parents, 2, + CLK_SET_RATE_NO_REPARENT, SLCR_GEM1_CLK_CTRL, 6, 1, 0, + &gem1clk_lock); clks[gem1] = clk_register_gate(NULL, clk_output_name[gem1], "gem1_emio_mux", CLK_SET_RATE_PARENT, SLCR_GEM1_CLK_CTRL, 0, 0, &gem1clk_lock); @@ -406,8 +413,9 @@ static void __init zynq_clk_setup(struct device_node *np) can_mio_mux_parents[i] = dummy_nm; } kfree(clk_name); - clk = clk_register_mux(NULL, "can_mux", periph_parents, 4, 0, - SLCR_CAN_CLK_CTRL, 4, 2, 0, &canclk_lock); + clk = clk_register_mux(NULL, "can_mux", periph_parents, 4, + CLK_SET_RATE_NO_REPARENT, SLCR_CAN_CLK_CTRL, 4, 2, 0, + &canclk_lock); clk = clk_register_divider(NULL, "can_div0", "can_mux", 0, SLCR_CAN_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, &canclk_lock); @@ -422,17 +430,21 @@ static void __init zynq_clk_setup(struct device_node *np) CLK_SET_RATE_PARENT, SLCR_CAN_CLK_CTRL, 1, 0, &canclk_lock); clk = clk_register_mux(NULL, "can0_mio_mux", - can_mio_mux_parents, 54, CLK_SET_RATE_PARENT, - SLCR_CAN_MIOCLK_CTRL, 0, 6, 0, &canmioclk_lock); + can_mio_mux_parents, 54, CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT, SLCR_CAN_MIOCLK_CTRL, 0, 6, 0, + &canmioclk_lock); clk = clk_register_mux(NULL, "can1_mio_mux", - can_mio_mux_parents, 54, CLK_SET_RATE_PARENT, - SLCR_CAN_MIOCLK_CTRL, 16, 6, 0, &canmioclk_lock); + can_mio_mux_parents, 54, CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT, SLCR_CAN_MIOCLK_CTRL, 16, 6, + 0, &canmioclk_lock); clks[can0] = clk_register_mux(NULL, clk_output_name[can0], - can0_mio_mux2_parents, 2, CLK_SET_RATE_PARENT, - SLCR_CAN_MIOCLK_CTRL, 6, 1, 0, &canmioclk_lock); + can0_mio_mux2_parents, 2, CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT, SLCR_CAN_MIOCLK_CTRL, 6, 1, 0, + &canmioclk_lock); clks[can1] = clk_register_mux(NULL, clk_output_name[can1], - can1_mio_mux2_parents, 2, CLK_SET_RATE_PARENT, - SLCR_CAN_MIOCLK_CTRL, 22, 1, 0, &canmioclk_lock); + can1_mio_mux2_parents, 2, CLK_SET_RATE_PARENT | + CLK_SET_RATE_NO_REPARENT, SLCR_CAN_MIOCLK_CTRL, 22, 1, + 0, &canmioclk_lock); for (i = 0; i < ARRAY_SIZE(dbgtrc_emio_input_names); i++) { int idx = of_property_match_string(np, "clock-names", @@ -441,13 +453,15 @@ static void __init zynq_clk_setup(struct device_node *np) dbg_emio_mux_parents[i + 1] = of_clk_get_parent_name(np, idx); } - clk = clk_register_mux(NULL, "dbg_mux", periph_parents, 4, 0, - SLCR_DBG_CLK_CTRL, 4, 2, 0, &dbgclk_lock); + clk = clk_register_mux(NULL, "dbg_mux", periph_parents, 4, + CLK_SET_RATE_NO_REPARENT, SLCR_DBG_CLK_CTRL, 4, 2, 0, + &dbgclk_lock); clk = clk_register_divider(NULL, "dbg_div", "dbg_mux", 0, SLCR_DBG_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, &dbgclk_lock); - clk = clk_register_mux(NULL, "dbg_emio_mux", dbg_emio_mux_parents, 2, 0, - SLCR_DBG_CLK_CTRL, 6, 1, 0, &dbgclk_lock); + clk = clk_register_mux(NULL, "dbg_emio_mux", dbg_emio_mux_parents, 2, + CLK_SET_RATE_NO_REPARENT, SLCR_DBG_CLK_CTRL, 6, 1, 0, + &dbgclk_lock); clks[dbg_trc] = clk_register_gate(NULL, clk_output_name[dbg_trc], "dbg_emio_mux", CLK_SET_RATE_PARENT, SLCR_DBG_CLK_CTRL, 0, 0, &dbgclk_lock); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 4f525b37c6fd..5c0bc3904c9b 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -27,6 +27,7 @@ #define CLK_IS_ROOT BIT(4) /* root clk, has no parent */ #define CLK_IS_BASIC BIT(5) /* Basic clk, can't do a to_clk_foo() */ #define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */ +#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */ struct clk_hw; -- cgit v1.2.3-71-gd317 From e366fdd72529c545ccf327569ee250c1673be221 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 29 Jul 2013 12:25:02 +0100 Subject: clk: clk-mux: implement remuxing on set_rate Implement clk-mux remuxing if the CLK_SET_RATE_NO_REPARENT flag isn't set. This implements determine_rate for clk-mux to propagate to each parent and to choose the best one (like clk-divider this chooses the parent which provides the fastest rate <= the requested rate). The determine_rate op is implemented as a core helper function so that it can be easily used by more complex clocks which incorporate muxes. Signed-off-by: James Hogan Reviewed-by: Stephen Boyd Cc: Mike Turquette Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Mike Turquette --- drivers/clk/clk-mux.c | 1 + drivers/clk/clk.c | 49 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/clk-provider.h | 3 +++ 3 files changed, 53 insertions(+) (limited to 'include') diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index b918dc370bd0..0811633fcc4d 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -104,6 +104,7 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) const struct clk_ops clk_mux_ops = { .get_parent = clk_mux_get_parent, .set_parent = clk_mux_set_parent, + .determine_rate = __clk_mux_determine_rate, }; EXPORT_SYMBOL_GPL(clk_mux_ops); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index f33f1ccf1b2f..bc020372106b 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -692,6 +692,55 @@ struct clk *__clk_lookup(const char *name) return NULL; } +/* + * Helper for finding best parent to provide a given frequency. This can be used + * directly as a determine_rate callback (e.g. for a mux), or from a more + * complex clock that may combine a mux with other operations. + */ +long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_p) +{ + struct clk *clk = hw->clk, *parent, *best_parent = NULL; + int i, num_parents; + unsigned long parent_rate, best = 0; + + /* if NO_REPARENT flag set, pass through to current parent */ + if (clk->flags & CLK_SET_RATE_NO_REPARENT) { + parent = clk->parent; + if (clk->flags & CLK_SET_RATE_PARENT) + best = __clk_round_rate(parent, rate); + else if (parent) + best = __clk_get_rate(parent); + else + best = __clk_get_rate(clk); + goto out; + } + + /* find the parent that can provide the fastest rate <= rate */ + num_parents = clk->num_parents; + for (i = 0; i < num_parents; i++) { + parent = clk_get_parent_by_index(clk, i); + if (!parent) + continue; + if (clk->flags & CLK_SET_RATE_PARENT) + parent_rate = __clk_round_rate(parent, rate); + else + parent_rate = __clk_get_rate(parent); + if (parent_rate <= rate && parent_rate > best) { + best_parent = parent; + best = parent_rate; + } + } + +out: + if (best_parent) + *best_parent_p = best_parent; + *best_parent_rate = best; + + return best; +} + /*** clk api ***/ void __clk_unprepare(struct clk *clk) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 5c0bc3904c9b..1f0285b2f422 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -436,6 +436,9 @@ unsigned long __clk_get_flags(struct clk *clk); bool __clk_is_prepared(struct clk *clk); bool __clk_is_enabled(struct clk *clk); struct clk *__clk_lookup(const char *name); +long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate, + struct clk **best_parent_p); /* * FIXME clock api without lock protection -- cgit v1.2.3-71-gd317 From 8851b9f1625ce0858e9b1bb0ae4a57d4b43178b1 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 6 Aug 2013 19:42:49 +0530 Subject: include: dt-binding: input: create a DT header defining key codes. Many of Key device tree bindings uses the constant number as key code which matches with kernel header key code and then comment as follows for reference/better readability: linux,code = <102>; /* KEY_HOME */ Create a DT header which defines all the key code so that DT key bindings can use it as follows: linux,code = ; Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Signed-off-by: Rob Herring --- include/dt-bindings/input/input.h | 525 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 525 insertions(+) create mode 100644 include/dt-bindings/input/input.h (limited to 'include') diff --git a/include/dt-bindings/input/input.h b/include/dt-bindings/input/input.h new file mode 100644 index 000000000000..042e7b3b6296 --- /dev/null +++ b/include/dt-bindings/input/input.h @@ -0,0 +1,525 @@ +/* + * This header provides constants for most input bindings. + * + * Most input bindings include key code, matrix key code format. + * In most cases, key code and matrix key code format uses + * the standard values/macro defined in this header. + */ + +#ifndef _DT_BINDINGS_INPUT_INPUT_H +#define _DT_BINDINGS_INPUT_INPUT_H + +#define KEY_RESERVED 0 +#define KEY_ESC 1 +#define KEY_1 2 +#define KEY_2 3 +#define KEY_3 4 +#define KEY_4 5 +#define KEY_5 6 +#define KEY_6 7 +#define KEY_7 8 +#define KEY_8 9 +#define KEY_9 10 +#define KEY_0 11 +#define KEY_MINUS 12 +#define KEY_EQUAL 13 +#define KEY_BACKSPACE 14 +#define KEY_TAB 15 +#define KEY_Q 16 +#define KEY_W 17 +#define KEY_E 18 +#define KEY_R 19 +#define KEY_T 20 +#define KEY_Y 21 +#define KEY_U 22 +#define KEY_I 23 +#define KEY_O 24 +#define KEY_P 25 +#define KEY_LEFTBRACE 26 +#define KEY_RIGHTBRACE 27 +#define KEY_ENTER 28 +#define KEY_LEFTCTRL 29 +#define KEY_A 30 +#define KEY_S 31 +#define KEY_D 32 +#define KEY_F 33 +#define KEY_G 34 +#define KEY_H 35 +#define KEY_J 36 +#define KEY_K 37 +#define KEY_L 38 +#define KEY_SEMICOLON 39 +#define KEY_APOSTROPHE 40 +#define KEY_GRAVE 41 +#define KEY_LEFTSHIFT 42 +#define KEY_BACKSLASH 43 +#define KEY_Z 44 +#define KEY_X 45 +#define KEY_C 46 +#define KEY_V 47 +#define KEY_B 48 +#define KEY_N 49 +#define KEY_M 50 +#define KEY_COMMA 51 +#define KEY_DOT 52 +#define KEY_SLASH 53 +#define KEY_RIGHTSHIFT 54 +#define KEY_KPASTERISK 55 +#define KEY_LEFTALT 56 +#define KEY_SPACE 57 +#define KEY_CAPSLOCK 58 +#define KEY_F1 59 +#define KEY_F2 60 +#define KEY_F3 61 +#define KEY_F4 62 +#define KEY_F5 63 +#define KEY_F6 64 +#define KEY_F7 65 +#define KEY_F8 66 +#define KEY_F9 67 +#define KEY_F10 68 +#define KEY_NUMLOCK 69 +#define KEY_SCROLLLOCK 70 +#define KEY_KP7 71 +#define KEY_KP8 72 +#define KEY_KP9 73 +#define KEY_KPMINUS 74 +#define KEY_KP4 75 +#define KEY_KP5 76 +#define KEY_KP6 77 +#define KEY_KPPLUS 78 +#define KEY_KP1 79 +#define KEY_KP2 80 +#define KEY_KP3 81 +#define KEY_KP0 82 +#define KEY_KPDOT 83 + +#define KEY_ZENKAKUHANKAKU 85 +#define KEY_102ND 86 +#define KEY_F11 87 +#define KEY_F12 88 +#define KEY_RO 89 +#define KEY_KATAKANA 90 +#define KEY_HIRAGANA 91 +#define KEY_HENKAN 92 +#define KEY_KATAKANAHIRAGANA 93 +#define KEY_MUHENKAN 94 +#define KEY_KPJPCOMMA 95 +#define KEY_KPENTER 96 +#define KEY_RIGHTCTRL 97 +#define KEY_KPSLASH 98 +#define KEY_SYSRQ 99 +#define KEY_RIGHTALT 100 +#define KEY_LINEFEED 101 +#define KEY_HOME 102 +#define KEY_UP 103 +#define KEY_PAGEUP 104 +#define KEY_LEFT 105 +#define KEY_RIGHT 106 +#define KEY_END 107 +#define KEY_DOWN 108 +#define KEY_PAGEDOWN 109 +#define KEY_INSERT 110 +#define KEY_DELETE 111 +#define KEY_MACRO 112 +#define KEY_MUTE 113 +#define KEY_VOLUMEDOWN 114 +#define KEY_VOLUMEUP 115 +#define KEY_POWER 116 /* SC System Power Down */ +#define KEY_KPEQUAL 117 +#define KEY_KPPLUSMINUS 118 +#define KEY_PAUSE 119 +#define KEY_SCALE 120 /* AL Compiz Scale (Expose) */ + +#define KEY_KPCOMMA 121 +#define KEY_HANGEUL 122 +#define KEY_HANGUEL KEY_HANGEUL +#define KEY_HANJA 123 +#define KEY_YEN 124 +#define KEY_LEFTMETA 125 +#define KEY_RIGHTMETA 126 +#define KEY_COMPOSE 127 + +#define KEY_STOP 128 /* AC Stop */ +#define KEY_AGAIN 129 +#define KEY_PROPS 130 /* AC Properties */ +#define KEY_UNDO 131 /* AC Undo */ +#define KEY_FRONT 132 +#define KEY_COPY 133 /* AC Copy */ +#define KEY_OPEN 134 /* AC Open */ +#define KEY_PASTE 135 /* AC Paste */ +#define KEY_FIND 136 /* AC Search */ +#define KEY_CUT 137 /* AC Cut */ +#define KEY_HELP 138 /* AL Integrated Help Center */ +#define KEY_MENU 139 /* Menu (show menu) */ +#define KEY_CALC 140 /* AL Calculator */ +#define KEY_SETUP 141 +#define KEY_SLEEP 142 /* SC System Sleep */ +#define KEY_WAKEUP 143 /* System Wake Up */ +#define KEY_FILE 144 /* AL Local Machine Browser */ +#define KEY_SENDFILE 145 +#define KEY_DELETEFILE 146 +#define KEY_XFER 147 +#define KEY_PROG1 148 +#define KEY_PROG2 149 +#define KEY_WWW 150 /* AL Internet Browser */ +#define KEY_MSDOS 151 +#define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */ +#define KEY_SCREENLOCK KEY_COFFEE +#define KEY_DIRECTION 153 +#define KEY_CYCLEWINDOWS 154 +#define KEY_MAIL 155 +#define KEY_BOOKMARKS 156 /* AC Bookmarks */ +#define KEY_COMPUTER 157 +#define KEY_BACK 158 /* AC Back */ +#define KEY_FORWARD 159 /* AC Forward */ +#define KEY_CLOSECD 160 +#define KEY_EJECTCD 161 +#define KEY_EJECTCLOSECD 162 +#define KEY_NEXTSONG 163 +#define KEY_PLAYPAUSE 164 +#define KEY_PREVIOUSSONG 165 +#define KEY_STOPCD 166 +#define KEY_RECORD 167 +#define KEY_REWIND 168 +#define KEY_PHONE 169 /* Media Select Telephone */ +#define KEY_ISO 170 +#define KEY_CONFIG 171 /* AL Consumer Control Configuration */ +#define KEY_HOMEPAGE 172 /* AC Home */ +#define KEY_REFRESH 173 /* AC Refresh */ +#define KEY_EXIT 174 /* AC Exit */ +#define KEY_MOVE 175 +#define KEY_EDIT 176 +#define KEY_SCROLLUP 177 +#define KEY_SCROLLDOWN 178 +#define KEY_KPLEFTPAREN 179 +#define KEY_KPRIGHTPAREN 180 +#define KEY_NEW 181 /* AC New */ +#define KEY_REDO 182 /* AC Redo/Repeat */ + +#define KEY_F13 183 +#define KEY_F14 184 +#define KEY_F15 185 +#define KEY_F16 186 +#define KEY_F17 187 +#define KEY_F18 188 +#define KEY_F19 189 +#define KEY_F20 190 +#define KEY_F21 191 +#define KEY_F22 192 +#define KEY_F23 193 +#define KEY_F24 194 + +#define KEY_PLAYCD 200 +#define KEY_PAUSECD 201 +#define KEY_PROG3 202 +#define KEY_PROG4 203 +#define KEY_DASHBOARD 204 /* AL Dashboard */ +#define KEY_SUSPEND 205 +#define KEY_CLOSE 206 /* AC Close */ +#define KEY_PLAY 207 +#define KEY_FASTFORWARD 208 +#define KEY_BASSBOOST 209 +#define KEY_PRINT 210 /* AC Print */ +#define KEY_HP 211 +#define KEY_CAMERA 212 +#define KEY_SOUND 213 +#define KEY_QUESTION 214 +#define KEY_EMAIL 215 +#define KEY_CHAT 216 +#define KEY_SEARCH 217 +#define KEY_CONNECT 218 +#define KEY_FINANCE 219 /* AL Checkbook/Finance */ +#define KEY_SPORT 220 +#define KEY_SHOP 221 +#define KEY_ALTERASE 222 +#define KEY_CANCEL 223 /* AC Cancel */ +#define KEY_BRIGHTNESSDOWN 224 +#define KEY_BRIGHTNESSUP 225 +#define KEY_MEDIA 226 + +#define KEY_SWITCHVIDEOMODE 227 /* Cycle between available video + outputs (Monitor/LCD/TV-out/etc) */ +#define KEY_KBDILLUMTOGGLE 228 +#define KEY_KBDILLUMDOWN 229 +#define KEY_KBDILLUMUP 230 + +#define KEY_SEND 231 /* AC Send */ +#define KEY_REPLY 232 /* AC Reply */ +#define KEY_FORWARDMAIL 233 /* AC Forward Msg */ +#define KEY_SAVE 234 /* AC Save */ +#define KEY_DOCUMENTS 235 + +#define KEY_BATTERY 236 + +#define KEY_BLUETOOTH 237 +#define KEY_WLAN 238 +#define KEY_UWB 239 + +#define KEY_UNKNOWN 240 + +#define KEY_VIDEO_NEXT 241 /* drive next video source */ +#define KEY_VIDEO_PREV 242 /* drive previous video source */ +#define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */ +#define KEY_BRIGHTNESS_ZERO 244 /* brightness off, use ambient */ +#define KEY_DISPLAY_OFF 245 /* display device to off state */ + +#define KEY_WIMAX 246 +#define KEY_RFKILL 247 /* Key that controls all radios */ + +#define KEY_MICMUTE 248 /* Mute / unmute the microphone */ + +/* Code 255 is reserved for special needs of AT keyboard driver */ + +#define BTN_MISC 0x100 +#define BTN_0 0x100 +#define BTN_1 0x101 +#define BTN_2 0x102 +#define BTN_3 0x103 +#define BTN_4 0x104 +#define BTN_5 0x105 +#define BTN_6 0x106 +#define BTN_7 0x107 +#define BTN_8 0x108 +#define BTN_9 0x109 + +#define BTN_MOUSE 0x110 +#define BTN_LEFT 0x110 +#define BTN_RIGHT 0x111 +#define BTN_MIDDLE 0x112 +#define BTN_SIDE 0x113 +#define BTN_EXTRA 0x114 +#define BTN_FORWARD 0x115 +#define BTN_BACK 0x116 +#define BTN_TASK 0x117 + +#define BTN_JOYSTICK 0x120 +#define BTN_TRIGGER 0x120 +#define BTN_THUMB 0x121 +#define BTN_THUMB2 0x122 +#define BTN_TOP 0x123 +#define BTN_TOP2 0x124 +#define BTN_PINKIE 0x125 +#define BTN_BASE 0x126 +#define BTN_BASE2 0x127 +#define BTN_BASE3 0x128 +#define BTN_BASE4 0x129 +#define BTN_BASE5 0x12a +#define BTN_BASE6 0x12b +#define BTN_DEAD 0x12f + +#define BTN_GAMEPAD 0x130 +#define BTN_SOUTH 0x130 +#define BTN_A BTN_SOUTH +#define BTN_EAST 0x131 +#define BTN_B BTN_EAST +#define BTN_C 0x132 +#define BTN_NORTH 0x133 +#define BTN_X BTN_NORTH +#define BTN_WEST 0x134 +#define BTN_Y BTN_WEST +#define BTN_Z 0x135 +#define BTN_TL 0x136 +#define BTN_TR 0x137 +#define BTN_TL2 0x138 +#define BTN_TR2 0x139 +#define BTN_SELECT 0x13a +#define BTN_START 0x13b +#define BTN_MODE 0x13c +#define BTN_THUMBL 0x13d +#define BTN_THUMBR 0x13e + +#define BTN_DIGI 0x140 +#define BTN_TOOL_PEN 0x140 +#define BTN_TOOL_RUBBER 0x141 +#define BTN_TOOL_BRUSH 0x142 +#define BTN_TOOL_PENCIL 0x143 +#define BTN_TOOL_AIRBRUSH 0x144 +#define BTN_TOOL_FINGER 0x145 +#define BTN_TOOL_MOUSE 0x146 +#define BTN_TOOL_LENS 0x147 +#define BTN_TOOL_QUINTTAP 0x148 /* Five fingers on trackpad */ +#define BTN_TOUCH 0x14a +#define BTN_STYLUS 0x14b +#define BTN_STYLUS2 0x14c +#define BTN_TOOL_DOUBLETAP 0x14d +#define BTN_TOOL_TRIPLETAP 0x14e +#define BTN_TOOL_QUADTAP 0x14f /* Four fingers on trackpad */ + +#define BTN_WHEEL 0x150 +#define BTN_GEAR_DOWN 0x150 +#define BTN_GEAR_UP 0x151 + +#define KEY_OK 0x160 +#define KEY_SELECT 0x161 +#define KEY_GOTO 0x162 +#define KEY_CLEAR 0x163 +#define KEY_POWER2 0x164 +#define KEY_OPTION 0x165 +#define KEY_INFO 0x166 /* AL OEM Features/Tips/Tutorial */ +#define KEY_TIME 0x167 +#define KEY_VENDOR 0x168 +#define KEY_ARCHIVE 0x169 +#define KEY_PROGRAM 0x16a /* Media Select Program Guide */ +#define KEY_CHANNEL 0x16b +#define KEY_FAVORITES 0x16c +#define KEY_EPG 0x16d +#define KEY_PVR 0x16e /* Media Select Home */ +#define KEY_MHP 0x16f +#define KEY_LANGUAGE 0x170 +#define KEY_TITLE 0x171 +#define KEY_SUBTITLE 0x172 +#define KEY_ANGLE 0x173 +#define KEY_ZOOM 0x174 +#define KEY_MODE 0x175 +#define KEY_KEYBOARD 0x176 +#define KEY_SCREEN 0x177 +#define KEY_PC 0x178 /* Media Select Computer */ +#define KEY_TV 0x179 /* Media Select TV */ +#define KEY_TV2 0x17a /* Media Select Cable */ +#define KEY_VCR 0x17b /* Media Select VCR */ +#define KEY_VCR2 0x17c /* VCR Plus */ +#define KEY_SAT 0x17d /* Media Select Satellite */ +#define KEY_SAT2 0x17e +#define KEY_CD 0x17f /* Media Select CD */ +#define KEY_TAPE 0x180 /* Media Select Tape */ +#define KEY_RADIO 0x181 +#define KEY_TUNER 0x182 /* Media Select Tuner */ +#define KEY_PLAYER 0x183 +#define KEY_TEXT 0x184 +#define KEY_DVD 0x185 /* Media Select DVD */ +#define KEY_AUX 0x186 +#define KEY_MP3 0x187 +#define KEY_AUDIO 0x188 /* AL Audio Browser */ +#define KEY_VIDEO 0x189 /* AL Movie Browser */ +#define KEY_DIRECTORY 0x18a +#define KEY_LIST 0x18b +#define KEY_MEMO 0x18c /* Media Select Messages */ +#define KEY_CALENDAR 0x18d +#define KEY_RED 0x18e +#define KEY_GREEN 0x18f +#define KEY_YELLOW 0x190 +#define KEY_BLUE 0x191 +#define KEY_CHANNELUP 0x192 /* Channel Increment */ +#define KEY_CHANNELDOWN 0x193 /* Channel Decrement */ +#define KEY_FIRST 0x194 +#define KEY_LAST 0x195 /* Recall Last */ +#define KEY_AB 0x196 +#define KEY_NEXT 0x197 +#define KEY_RESTART 0x198 +#define KEY_SLOW 0x199 +#define KEY_SHUFFLE 0x19a +#define KEY_BREAK 0x19b +#define KEY_PREVIOUS 0x19c +#define KEY_DIGITS 0x19d +#define KEY_TEEN 0x19e +#define KEY_TWEN 0x19f +#define KEY_VIDEOPHONE 0x1a0 /* Media Select Video Phone */ +#define KEY_GAMES 0x1a1 /* Media Select Games */ +#define KEY_ZOOMIN 0x1a2 /* AC Zoom In */ +#define KEY_ZOOMOUT 0x1a3 /* AC Zoom Out */ +#define KEY_ZOOMRESET 0x1a4 /* AC Zoom */ +#define KEY_WORDPROCESSOR 0x1a5 /* AL Word Processor */ +#define KEY_EDITOR 0x1a6 /* AL Text Editor */ +#define KEY_SPREADSHEET 0x1a7 /* AL Spreadsheet */ +#define KEY_GRAPHICSEDITOR 0x1a8 /* AL Graphics Editor */ +#define KEY_PRESENTATION 0x1a9 /* AL Presentation App */ +#define KEY_DATABASE 0x1aa /* AL Database App */ +#define KEY_NEWS 0x1ab /* AL Newsreader */ +#define KEY_VOICEMAIL 0x1ac /* AL Voicemail */ +#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */ +#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */ +#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */ +#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */ +#define KEY_LOGOFF 0x1b1 /* AL Logoff */ + +#define KEY_DOLLAR 0x1b2 +#define KEY_EURO 0x1b3 + +#define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */ +#define KEY_FRAMEFORWARD 0x1b5 +#define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */ +#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */ +#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */ +#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */ +#define KEY_IMAGES 0x1ba /* AL Image Browser */ + +#define KEY_DEL_EOL 0x1c0 +#define KEY_DEL_EOS 0x1c1 +#define KEY_INS_LINE 0x1c2 +#define KEY_DEL_LINE 0x1c3 + +#define KEY_FN 0x1d0 +#define KEY_FN_ESC 0x1d1 +#define KEY_FN_F1 0x1d2 +#define KEY_FN_F2 0x1d3 +#define KEY_FN_F3 0x1d4 +#define KEY_FN_F4 0x1d5 +#define KEY_FN_F5 0x1d6 +#define KEY_FN_F6 0x1d7 +#define KEY_FN_F7 0x1d8 +#define KEY_FN_F8 0x1d9 +#define KEY_FN_F9 0x1da +#define KEY_FN_F10 0x1db +#define KEY_FN_F11 0x1dc +#define KEY_FN_F12 0x1dd +#define KEY_FN_1 0x1de +#define KEY_FN_2 0x1df +#define KEY_FN_D 0x1e0 +#define KEY_FN_E 0x1e1 +#define KEY_FN_F 0x1e2 +#define KEY_FN_S 0x1e3 +#define KEY_FN_B 0x1e4 + +#define KEY_BRL_DOT1 0x1f1 +#define KEY_BRL_DOT2 0x1f2 +#define KEY_BRL_DOT3 0x1f3 +#define KEY_BRL_DOT4 0x1f4 +#define KEY_BRL_DOT5 0x1f5 +#define KEY_BRL_DOT6 0x1f6 +#define KEY_BRL_DOT7 0x1f7 +#define KEY_BRL_DOT8 0x1f8 +#define KEY_BRL_DOT9 0x1f9 +#define KEY_BRL_DOT10 0x1fa + +#define KEY_NUMERIC_0 0x200 /* used by phones, remote controls, */ +#define KEY_NUMERIC_1 0x201 /* and other keypads */ +#define KEY_NUMERIC_2 0x202 +#define KEY_NUMERIC_3 0x203 +#define KEY_NUMERIC_4 0x204 +#define KEY_NUMERIC_5 0x205 +#define KEY_NUMERIC_6 0x206 +#define KEY_NUMERIC_7 0x207 +#define KEY_NUMERIC_8 0x208 +#define KEY_NUMERIC_9 0x209 +#define KEY_NUMERIC_STAR 0x20a +#define KEY_NUMERIC_POUND 0x20b + +#define KEY_CAMERA_FOCUS 0x210 +#define KEY_WPS_BUTTON 0x211 /* WiFi Protected Setup key */ + +#define KEY_TOUCHPAD_TOGGLE 0x212 /* Request switch touchpad on or off */ +#define KEY_TOUCHPAD_ON 0x213 +#define KEY_TOUCHPAD_OFF 0x214 + +#define KEY_CAMERA_ZOOMIN 0x215 +#define KEY_CAMERA_ZOOMOUT 0x216 +#define KEY_CAMERA_UP 0x217 +#define KEY_CAMERA_DOWN 0x218 +#define KEY_CAMERA_LEFT 0x219 +#define KEY_CAMERA_RIGHT 0x21a + +#define KEY_ATTENDANT_ON 0x21b +#define KEY_ATTENDANT_OFF 0x21c +#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */ +#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */ + +#define BTN_DPAD_UP 0x220 +#define BTN_DPAD_DOWN 0x221 +#define BTN_DPAD_LEFT 0x222 +#define BTN_DPAD_RIGHT 0x223 + +#define MATRIX_KEY(row, col, code) \ + ((((row) & 0xFF) << 24) | (((col) & 0xFF) << 16) | ((code) & 0xFFFF)) + +#endif /* _DT_BINDINGS_INPUT_INPUT_H */ -- cgit v1.2.3-71-gd317 From 36ec66e0333355112148549f9846adcc0909482e Mon Sep 17 00:00:00 2001 From: Krystian Garbaciak Date: Mon, 29 Jul 2013 19:00:42 +0200 Subject: mfd: da9063: Add register definitions for DA9063 driver This patch adds register definitions for the DA9063 PMIC. They will be used by the following DA9063 mfd core driver and functional module drivers. Signed-off-by: Krystian Garbaciak Signed-off-by: Philipp Zabel Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- include/linux/mfd/da9063/registers.h | 1028 ++++++++++++++++++++++++++++++++++ 1 file changed, 1028 insertions(+) create mode 100644 include/linux/mfd/da9063/registers.h (limited to 'include') diff --git a/include/linux/mfd/da9063/registers.h b/include/linux/mfd/da9063/registers.h new file mode 100644 index 000000000000..5834813fb5f3 --- /dev/null +++ b/include/linux/mfd/da9063/registers.h @@ -0,0 +1,1028 @@ +/* + * Registers definition for DA9063 modules + * + * Copyright 2012 Dialog Semiconductor Ltd. + * + * Author: Michal Hajduk + * Krystian Garbaciak + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef _DA9063_REG_H +#define _DA9063_REG_H + +#define DA9063_I2C_PAGE_SEL_SHIFT 1 + +#define DA9063_EVENT_REG_NUM 4 +#define DA9210_EVENT_REG_NUM 2 +#define DA9063_EXT_EVENT_REG_NUM (DA9063_EVENT_REG_NUM + \ + DA9210_EVENT_REG_NUM) + +/* Page selection I2C or SPI always in the begining of any page. */ +/* Page 0 : I2C access 0x000 - 0x0FF SPI access 0x000 - 0x07F */ +/* Page 1 : SPI access 0x080 - 0x0FF */ +/* Page 2 : I2C access 0x100 - 0x1FF SPI access 0x100 - 0x17F */ +/* Page 3 : SPI access 0x180 - 0x1FF */ +#define DA9063_REG_PAGE_CON 0x00 + +/* System Control and Event Registers */ +#define DA9063_REG_STATUS_A 0x01 +#define DA9063_REG_STATUS_B 0x02 +#define DA9063_REG_STATUS_C 0x03 +#define DA9063_REG_STATUS_D 0x04 +#define DA9063_REG_FAULT_LOG 0x05 +#define DA9063_REG_EVENT_A 0x06 +#define DA9063_REG_EVENT_B 0x07 +#define DA9063_REG_EVENT_C 0x08 +#define DA9063_REG_EVENT_D 0x09 +#define DA9063_REG_IRQ_MASK_A 0x0A +#define DA9063_REG_IRQ_MASK_B 0x0B +#define DA9063_REG_IRQ_MASK_C 0x0C +#define DA9063_REG_IRQ_MASK_D 0x0D +#define DA9063_REG_CONTROL_A 0x0E +#define DA9063_REG_CONTROL_B 0x0F +#define DA9063_REG_CONTROL_C 0x10 +#define DA9063_REG_CONTROL_D 0x11 +#define DA9063_REG_CONTROL_E 0x12 +#define DA9063_REG_CONTROL_F 0x13 +#define DA9063_REG_PD_DIS 0x14 + +/* GPIO Control Registers */ +#define DA9063_REG_GPIO_0_1 0x15 +#define DA9063_REG_GPIO_2_3 0x16 +#define DA9063_REG_GPIO_4_5 0x17 +#define DA9063_REG_GPIO_6_7 0x18 +#define DA9063_REG_GPIO_8_9 0x19 +#define DA9063_REG_GPIO_10_11 0x1A +#define DA9063_REG_GPIO_12_13 0x1B +#define DA9063_REG_GPIO_14_15 0x1C +#define DA9063_REG_GPIO_MODE_0_7 0x1D +#define DA9063_REG_GPIO_MODE_8_15 0x1E +#define DA9063_REG_GPIO_SWITCH_CONT 0x1F + +/* Regulator Control Registers */ +#define DA9063_REG_BCORE2_CONT 0x20 +#define DA9063_REG_BCORE1_CONT 0x21 +#define DA9063_REG_BPRO_CONT 0x22 +#define DA9063_REG_BMEM_CONT 0x23 +#define DA9063_REG_BIO_CONT 0x24 +#define DA9063_REG_BPERI_CONT 0x25 +#define DA9063_REG_LDO1_CONT 0x26 +#define DA9063_REG_LDO2_CONT 0x27 +#define DA9063_REG_LDO3_CONT 0x28 +#define DA9063_REG_LDO4_CONT 0x29 +#define DA9063_REG_LDO5_CONT 0x2A +#define DA9063_REG_LDO6_CONT 0x2B +#define DA9063_REG_LDO7_CONT 0x2C +#define DA9063_REG_LDO8_CONT 0x2D +#define DA9063_REG_LDO9_CONT 0x2E +#define DA9063_REG_LDO10_CONT 0x2F +#define DA9063_REG_LDO11_CONT 0x30 +#define DA9063_REG_VIB 0x31 +#define DA9063_REG_DVC_1 0x32 +#define DA9063_REG_DVC_2 0x33 + +/* GP-ADC Control Registers */ +#define DA9063_REG_ADC_MAN 0x34 +#define DA9063_REG_ADC_CONT 0x35 +#define DA9063_REG_VSYS_MON 0x36 +#define DA9063_REG_ADC_RES_L 0x37 +#define DA9063_REG_ADC_RES_H 0x38 +#define DA9063_REG_VSYS_RES 0x39 +#define DA9063_REG_ADCIN1_RES 0x3A +#define DA9063_REG_ADCIN2_RES 0x3B +#define DA9063_REG_ADCIN3_RES 0x3C +#define DA9063_REG_MON1_RES 0x3D +#define DA9063_REG_MON2_RES 0x3E +#define DA9063_REG_MON3_RES 0x3F + +/* RTC Calendar and Alarm Registers */ +#define DA9063_REG_COUNT_S 0x40 +#define DA9063_REG_COUNT_MI 0x41 +#define DA9063_REG_COUNT_H 0x42 +#define DA9063_REG_COUNT_D 0x43 +#define DA9063_REG_COUNT_MO 0x44 +#define DA9063_REG_COUNT_Y 0x45 +#define DA9063_REG_ALARM_MI 0x46 +#define DA9063_REG_ALARM_H 0x47 +#define DA9063_REG_ALARM_D 0x48 +#define DA9063_REG_ALARM_MO 0x49 +#define DA9063_REG_ALARM_Y 0x4A +#define DA9063_REG_SECOND_A 0x4B +#define DA9063_REG_SECOND_B 0x4C +#define DA9063_REG_SECOND_C 0x4D +#define DA9063_REG_SECOND_D 0x4E + +/* Sequencer Control Registers */ +#define DA9063_REG_SEQ 0x81 +#define DA9063_REG_SEQ_TIMER 0x82 +#define DA9063_REG_ID_2_1 0x83 +#define DA9063_REG_ID_4_3 0x84 +#define DA9063_REG_ID_6_5 0x85 +#define DA9063_REG_ID_8_7 0x86 +#define DA9063_REG_ID_10_9 0x87 +#define DA9063_REG_ID_12_11 0x88 +#define DA9063_REG_ID_14_13 0x89 +#define DA9063_REG_ID_16_15 0x8A +#define DA9063_REG_ID_18_17 0x8B +#define DA9063_REG_ID_20_19 0x8C +#define DA9063_REG_ID_22_21 0x8D +#define DA9063_REG_ID_24_23 0x8E +#define DA9063_REG_ID_26_25 0x8F +#define DA9063_REG_ID_28_27 0x90 +#define DA9063_REG_ID_30_29 0x91 +#define DA9063_REG_ID_32_31 0x92 +#define DA9063_REG_SEQ_A 0x95 +#define DA9063_REG_SEQ_B 0x96 +#define DA9063_REG_WAIT 0x97 +#define DA9063_REG_EN_32K 0x98 +#define DA9063_REG_RESET 0x99 + +/* Regulator Setting Registers */ +#define DA9063_REG_BUCK_ILIM_A 0x9A +#define DA9063_REG_BUCK_ILIM_B 0x9B +#define DA9063_REG_BUCK_ILIM_C 0x9C +#define DA9063_REG_BCORE2_CFG 0x9D +#define DA9063_REG_BCORE1_CFG 0x9E +#define DA9063_REG_BPRO_CFG 0x9F +#define DA9063_REG_BIO_CFG 0xA0 +#define DA9063_REG_BMEM_CFG 0xA1 +#define DA9063_REG_BPERI_CFG 0xA2 +#define DA9063_REG_VBCORE2_A 0xA3 +#define DA9063_REG_VBCORE1_A 0xA4 +#define DA9063_REG_VBPRO_A 0xA5 +#define DA9063_REG_VBMEM_A 0xA6 +#define DA9063_REG_VBIO_A 0xA7 +#define DA9063_REG_VBPERI_A 0xA8 +#define DA9063_REG_VLDO1_A 0xA9 +#define DA9063_REG_VLDO2_A 0xAA +#define DA9063_REG_VLDO3_A 0xAB +#define DA9063_REG_VLDO4_A 0xAC +#define DA9063_REG_VLDO5_A 0xAD +#define DA9063_REG_VLDO6_A 0xAE +#define DA9063_REG_VLDO7_A 0xAF +#define DA9063_REG_VLDO8_A 0xB0 +#define DA9063_REG_VLDO9_A 0xB1 +#define DA9063_REG_VLDO10_A 0xB2 +#define DA9063_REG_VLDO11_A 0xB3 +#define DA9063_REG_VBCORE2_B 0xB4 +#define DA9063_REG_VBCORE1_B 0xB5 +#define DA9063_REG_VBPRO_B 0xB6 +#define DA9063_REG_VBMEM_B 0xB7 +#define DA9063_REG_VBIO_B 0xB8 +#define DA9063_REG_VBPERI_B 0xB9 +#define DA9063_REG_VLDO1_B 0xBA +#define DA9063_REG_VLDO2_B 0xBB +#define DA9063_REG_VLDO3_B 0xBC +#define DA9063_REG_VLDO4_B 0xBD +#define DA9063_REG_VLDO5_B 0xBE +#define DA9063_REG_VLDO6_B 0xBF +#define DA9063_REG_VLDO7_B 0xC0 +#define DA9063_REG_VLDO8_B 0xC1 +#define DA9063_REG_VLDO9_B 0xC2 +#define DA9063_REG_VLDO10_B 0xC3 +#define DA9063_REG_VLDO11_B 0xC4 + +/* Backup Battery Charger Control Register */ +#define DA9063_REG_BBAT_CONT 0xC5 + +/* GPIO PWM (LED) */ +#define DA9063_REG_GPO11_LED 0xC6 +#define DA9063_REG_GPO14_LED 0xC7 +#define DA9063_REG_GPO15_LED 0xC8 + +/* GP-ADC Threshold Registers */ +#define DA9063_REG_ADC_CFG 0xC9 +#define DA9063_REG_AUTO1_HIGH 0xCA +#define DA9063_REG_AUTO1_LOW 0xCB +#define DA9063_REG_AUTO2_HIGH 0xCC +#define DA9063_REG_AUTO2_LOW 0xCD +#define DA9063_REG_AUTO3_HIGH 0xCE +#define DA9063_REG_AUTO3_LOW 0xCF + +/* DA9063 Configuration registers */ +/* OTP */ +#define DA9063_REG_OPT_COUNT 0x101 +#define DA9063_REG_OPT_ADDR 0x102 +#define DA9063_REG_OPT_DATA 0x103 + +/* Customer Trim and Configuration */ +#define DA9063_REG_T_OFFSET 0x104 +#define DA9063_REG_INTERFACE 0x105 +#define DA9063_REG_CONFIG_A 0x106 +#define DA9063_REG_CONFIG_B 0x107 +#define DA9063_REG_CONFIG_C 0x108 +#define DA9063_REG_CONFIG_D 0x109 +#define DA9063_REG_CONFIG_E 0x10A +#define DA9063_REG_CONFIG_F 0x10B +#define DA9063_REG_CONFIG_G 0x10C +#define DA9063_REG_CONFIG_H 0x10D +#define DA9063_REG_CONFIG_I 0x10E +#define DA9063_REG_CONFIG_J 0x10F +#define DA9063_REG_CONFIG_K 0x110 +#define DA9063_REG_CONFIG_L 0x111 +#define DA9063_REG_MON_REG_1 0x112 +#define DA9063_REG_MON_REG_2 0x113 +#define DA9063_REG_MON_REG_3 0x114 +#define DA9063_REG_MON_REG_4 0x115 +#define DA9063_REG_MON_REG_5 0x116 +#define DA9063_REG_MON_REG_6 0x117 +#define DA9063_REG_TRIM_CLDR 0x118 + +/* General Purpose Registers */ +#define DA9063_REG_GP_ID_0 0x119 +#define DA9063_REG_GP_ID_1 0x11A +#define DA9063_REG_GP_ID_2 0x11B +#define DA9063_REG_GP_ID_3 0x11C +#define DA9063_REG_GP_ID_4 0x11D +#define DA9063_REG_GP_ID_5 0x11E +#define DA9063_REG_GP_ID_6 0x11F +#define DA9063_REG_GP_ID_7 0x120 +#define DA9063_REG_GP_ID_8 0x121 +#define DA9063_REG_GP_ID_9 0x122 +#define DA9063_REG_GP_ID_10 0x123 +#define DA9063_REG_GP_ID_11 0x124 +#define DA9063_REG_GP_ID_12 0x125 +#define DA9063_REG_GP_ID_13 0x126 +#define DA9063_REG_GP_ID_14 0x127 +#define DA9063_REG_GP_ID_15 0x128 +#define DA9063_REG_GP_ID_16 0x129 +#define DA9063_REG_GP_ID_17 0x12A +#define DA9063_REG_GP_ID_18 0x12B +#define DA9063_REG_GP_ID_19 0x12C + +/* Chip ID and variant */ +#define DA9063_REG_CHIP_ID 0x181 +#define DA9063_REG_CHIP_VARIANT 0x182 + +/* + * PMIC registers bits + */ +/* DA9063_REG_PAGE_CON (addr=0x00) */ +#define DA9063_PEG_PAGE_SHIFT 0 +#define DA9063_REG_PAGE_MASK 0x07 +#define DA9063_REG_PAGE0 0x00 +#define DA9063_REG_PAGE2 0x02 +#define DA9063_PAGE_WRITE_MODE 0x00 +#define DA9063_REPEAT_WRITE_MODE 0x40 +#define DA9063_PAGE_REVERT 0x80 + +/* DA9063_REG_STATUS_A (addr=0x01) */ +#define DA9063_NONKEY 0x01 +#define DA9063_WAKE 0x02 +#define DA9063_DVC_BUSY 0x04 +#define DA9063_COMP_1V2 0x08 + +/* DA9063_REG_STATUS_B (addr=0x02) */ +#define DA9063_GPI0 0x01 +#define DA9063_GPI1 0x02 +#define DA9063_GPI2 0x04 +#define DA9063_GPI3 0x08 +#define DA9063_GPI4 0x10 +#define DA9063_GPI5 0x20 +#define DA9063_GPI6 0x40 +#define DA9063_GPI7 0x80 + +/* DA9063_REG_STATUS_C (addr=0x03) */ +#define DA9063_GPI8 0x01 +#define DA9063_GPI9 0x02 +#define DA9063_GPI10 0x04 +#define DA9063_GPI11 0x08 +#define DA9063_GPI12 0x10 +#define DA9063_GPI13 0x20 +#define DA9063_GPI14 0x40 +#define DA9063_GPI15 0x80 + +/* DA9063_REG_STATUS_D (addr=0x04) */ +#define DA9063_LDO3_LIM 0x08 +#define DA9063_LDO4_LIM 0x10 +#define DA9063_LDO7_LIM 0x20 +#define DA9063_LDO8_LIM 0x40 +#define DA9063_LDO11_LIM 0x80 + +/* DA9063_REG_FAULT_LOG (addr=0x05) */ +#define DA9063_TWD_ERROR 0x01 +#define DA9063_POR 0x02 +#define DA9063_VDD_FAULT 0x04 +#define DA9063_VDD_START 0x08 +#define DA9063_TEMP_CRIT 0x10 +#define DA9063_KEY_RESET 0x20 +#define DA9063_NSHUTDOWN 0x40 +#define DA9063_WAIT_SHUT 0x80 + +/* DA9063_REG_EVENT_A (addr=0x06) */ +#define DA9063_E_NONKEY 0x01 +#define DA9063_E_ALARM 0x02 +#define DA9063_E_TICK 0x04 +#define DA9063_E_ADC_RDY 0x08 +#define DA9063_E_SEQ_RDY 0x10 +#define DA9063_EVENTS_B 0x20 +#define DA9063_EVENTS_C 0x40 +#define DA9063_EVENTS_D 0x80 + +/* DA9063_REG_EVENT_B (addr=0x07) */ +#define DA9063_E_WAKE 0x01 +#define DA9063_E_TEMP 0x02 +#define DA9063_E_COMP_1V2 0x04 +#define DA9063_E_LDO_LIM 0x08 +#define DA9063_E_REG_UVOV 0x10 +#define DA9063_E_DVC_RDY 0x20 +#define DA9063_E_VDD_MON 0x40 +#define DA9063_E_VDD_WARN 0x80 + +/* DA9063_REG_EVENT_C (addr=0x08) */ +#define DA9063_E_GPI0 0x01 +#define DA9063_E_GPI1 0x02 +#define DA9063_E_GPI2 0x04 +#define DA9063_E_GPI3 0x08 +#define DA9063_E_GPI4 0x10 +#define DA9063_E_GPI5 0x20 +#define DA9063_E_GPI6 0x40 +#define DA9063_E_GPI7 0x80 + +/* DA9063_REG_EVENT_D (addr=0x09) */ +#define DA9063_E_GPI8 0x01 +#define DA9063_E_GPI9 0x02 +#define DA9063_E_GPI10 0x04 +#define DA9063_E_GPI11 0x08 +#define DA9063_E_GPI12 0x10 +#define DA9063_E_GPI13 0x20 +#define DA9063_E_GPI14 0x40 +#define DA9063_E_GPI15 0x80 + +/* DA9063_REG_IRQ_MASK_A (addr=0x0A) */ +#define DA9063_M_ONKEY 0x01 +#define DA9063_M_ALARM 0x02 +#define DA9063_M_TICK 0x04 +#define DA9063_M_ADC_RDY 0x08 +#define DA9063_M_SEQ_RDY 0x10 + +/* DA9063_REG_IRQ_MASK_B (addr=0x0B) */ +#define DA9063_M_WAKE 0x01 +#define DA9063_M_TEMP 0x02 +#define DA9063_M_COMP_1V2 0x04 +#define DA9063_M_LDO_LIM 0x08 +#define DA9063_M_UVOV 0x10 +#define DA9063_M_DVC_RDY 0x20 +#define DA9063_M_VDD_MON 0x40 +#define DA9063_M_VDD_WARN 0x80 + +/* DA9063_REG_IRQ_MASK_C (addr=0x0C) */ +#define DA9063_M_GPI0 0x01 +#define DA9063_M_GPI1 0x02 +#define DA9063_M_GPI2 0x04 +#define DA9063_M_GPI3 0x08 +#define DA9063_M_GPI4 0x10 +#define DA9063_M_GPI5 0x20 +#define DA9063_M_GPI6 0x40 +#define DA9063_M_GPI7 0x80 + +/* DA9063_REG_IRQ_MASK_D (addr=0x0D) */ +#define DA9063_M_GPI8 0x01 +#define DA9063_M_GPI9 0x02 +#define DA9063_M_GPI10 0x04 +#define DA9063_M_GPI11 0x08 +#define DA9063_M_GPI12 0x10 +#define DA9063_M_GPI13 0x20 +#define DA9063_M_GPI14 0x40 +#define DA9063_M_GPI15 0x80 + +/* DA9063_REG_CONTROL_A (addr=0x0E) */ +#define DA9063_SYSTEM_EN 0x01 +#define DA9063_POWER_EN 0x02 +#define DA9063_POWER1_EN 0x04 +#define DA9063_STANDBY 0x08 +#define DA9063_M_SYSTEM_EN 0x10 +#define DA9063_M_POWER_EN 0x20 +#define DA9063_M_POWER1_EN 0x40 +#define DA9063_CP_EN 0x80 + +/* DA9063_REG_CONTROL_B (addr=0x0F) */ +#define DA9063_CHG_SEL 0x01 +#define DA9063_WATCHDOG_PD 0x02 +#define DA9063_NRES_MODE 0x08 +#define DA9063_NONKEY_LOCK 0x10 + +/* DA9063_REG_CONTROL_C (addr=0x10) */ +#define DA9063_DEBOUNCING_MASK 0x07 +#define DA9063_DEBOUNCING_OFF 0x0 +#define DA9063_DEBOUNCING_0MS1 0x1 +#define DA9063_DEBOUNCING_1MS 0x2 +#define DA9063_DEBOUNCING_10MS24 0x3 +#define DA9063_DEBOUNCING_51MS2 0x4 +#define DA9063_DEBOUNCING_256MS 0x5 +#define DA9063_DEBOUNCING_512MS 0x6 +#define DA9063_DEBOUNCING_1024MS 0x7 + +#define DA9063_AUTO_BOOT 0x08 +#define DA9063_OTPREAD_EN 0x10 +#define DA9063_SLEW_RATE_MASK 0x60 +#define DA9063_SLEW_RATE_4US 0x00 +#define DA9063_SLEW_RATE_3US 0x20 +#define DA9063_SLEW_RATE_1US 0x40 +#define DA9063_SLEW_RATE_0US5 0x60 +#define DA9063_DEF_SUPPLY 0x80 + +/* DA9063_REG_CONTROL_D (addr=0x11) */ +#define DA9063_TWDSCALE_MASK 0x07 +#define DA9063_BLINK_FRQ_MASK 0x38 +#define DA9063_BLINK_FRQ_OFF 0x00 +#define DA9063_BLINK_FRQ_1S0 0x08 +#define DA9063_BLINK_FRQ_2S0 0x10 +#define DA9063_BLINK_FRQ_4S0 0x18 +#define DA9063_BLINK_FRQ_0S18 0x20 +#define DA9063_BLINK_FRQ_2S0_VDD 0x28 +#define DA9063_BLINK_FRQ_4S0_VDD 0x30 +#define DA9063_BLINK_FRQ_0S18_VDD 0x38 + +#define DA9063_BLINK_DUR_MASK 0xC0 +#define DA9063_BLINK_DUR_10MS 0x00 +#define DA9063_BLINK_DUR_20MS 0x40 +#define DA9063_BLINK_DUR_40MS 0x80 +#define DA9063_BLINK_DUR_20MSDBL 0xC0 + +/* DA9063_REG_CONTROL_E (addr=0x12) */ +#define DA9063_RTC_MODE_PD 0x01 +#define DA9063_RTC_MODE_SD 0x02 +#define DA9063_RTC_EN 0x04 +#define DA9063_ECO_MODE 0x08 +#define DA9063_PM_FB1_PIN 0x10 +#define DA9063_PM_FB2_PIN 0x20 +#define DA9063_PM_FB3_PIN 0x40 +#define DA9063_V_LOCK 0x80 + +/* DA9063_REG_CONTROL_F (addr=0x13) */ +#define DA9063_WATCHDOG 0x01 +#define DA9063_SHUTDOWN 0x02 +#define DA9063_WAKE_UP 0x04 + +/* DA9063_REG_PD_DIS (addr=0x14) */ +#define DA9063_GPI_DIS 0x01 +#define DA9063_GPADC_PAUSE 0x02 +#define DA9063_PMIF_DIS 0x04 +#define DA9063_HS2WIRE_DIS 0x08 +#define DA9063_BBAT_DIS 0x20 +#define DA9063_OUT_32K_PAUSE 0x40 +#define DA9063_PMCONT_DIS 0x80 + +/* DA9063_REG_GPIO_0_1 (addr=0x15) */ +#define DA9063_GPIO0_PIN_MASK 0x03 +#define DA9063_GPIO0_PIN_ADCIN1 0x00 +#define DA9063_GPIO0_PIN_GPI 0x01 +#define DA9063_GPIO0_PIN_GPO_OD 0x02 +#define DA9063_GPIO0_PIN_GPO 0x03 +#define DA9063_GPIO0_TYPE 0x04 +#define DA9063_GPIO0_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO0_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO0_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO0_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO0_NO_WAKEUP 0x08 +#define DA9063_GPIO1_PIN_MASK 0x30 +#define DA9063_GPIO1_PIN_ADCIN2_COMP 0x00 +#define DA9063_GPIO1_PIN_GPI 0x10 +#define DA9063_GPIO1_PIN_GPO_OD 0x20 +#define DA9063_GPIO1_PIN_GPO 0x30 +#define DA9063_GPIO1_TYPE 0x40 +#define DA9063_GPIO1_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO1_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO1_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO1_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO1_NO_WAKEUP 0x80 + +/* DA9063_REG_GPIO_2_3 (addr=0x16) */ +#define DA9063_GPIO2_PIN_MASK 0x03 +#define DA9063_GPIO2_PIN_ADCIN3 0x00 +#define DA9063_GPIO2_PIN_GPI 0x01 +#define DA9063_GPIO2_PIN_GPO_PSS 0x02 +#define DA9063_GPIO2_PIN_GPO 0x03 +#define DA9063_GPIO2_TYPE 0x04 +#define DA9063_GPIO2_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO2_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO2_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO2_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO2_NO_WAKEUP 0x08 +#define DA9063_GPIO3_PIN_MASK 0x30 +#define DA9063_GPIO3_PIN_CORE_SW_G 0x00 +#define DA9063_GPIO3_PIN_GPI 0x10 +#define DA9063_GPIO3_PIN_GPO_OD 0x20 +#define DA9063_GPIO3_PIN_GPO 0x30 +#define DA9063_GPIO3_TYPE 0x40 +#define DA9063_GPIO3_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO3_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO3_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO3_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO3_NO_WAKEUP 0x80 + +/* DA9063_REG_GPIO_4_5 (addr=0x17) */ +#define DA9063_GPIO4_PIN_MASK 0x03 +#define DA9063_GPIO4_PIN_CORE_SW_S 0x00 +#define DA9063_GPIO4_PIN_GPI 0x01 +#define DA9063_GPIO4_PIN_GPO_OD 0x02 +#define DA9063_GPIO4_PIN_GPO 0x03 +#define DA9063_GPIO4_TYPE 0x04 +#define DA9063_GPIO4_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO4_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO4_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO4_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO4_NO_WAKEUP 0x08 +#define DA9063_GPIO5_PIN_MASK 0x30 +#define DA9063_GPIO5_PIN_PERI_SW_G 0x00 +#define DA9063_GPIO5_PIN_GPI 0x10 +#define DA9063_GPIO5_PIN_GPO_OD 0x20 +#define DA9063_GPIO5_PIN_GPO 0x30 +#define DA9063_GPIO5_TYPE 0x40 +#define DA9063_GPIO5_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO5_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO5_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO5_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO5_NO_WAKEUP 0x80 + +/* DA9063_REG_GPIO_6_7 (addr=0x18) */ +#define DA9063_GPIO6_PIN_MASK 0x03 +#define DA9063_GPIO6_PIN_PERI_SW_S 0x00 +#define DA9063_GPIO6_PIN_GPI 0x01 +#define DA9063_GPIO6_PIN_GPO_OD 0x02 +#define DA9063_GPIO6_PIN_GPO 0x03 +#define DA9063_GPIO6_TYPE 0x04 +#define DA9063_GPIO6_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO6_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO6_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO6_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO6_NO_WAKEUP 0x08 +#define DA9063_GPIO7_PIN_MASK 0x30 +#define DA9063_GPIO7_PIN_GPI 0x10 +#define DA9063_GPIO7_PIN_GPO_PSS 0x20 +#define DA9063_GPIO7_PIN_GPO 0x30 +#define DA9063_GPIO7_TYPE 0x40 +#define DA9063_GPIO7_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO7_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO7_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO7_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO7_NO_WAKEUP 0x80 + +/* DA9063_REG_GPIO_8_9 (addr=0x19) */ +#define DA9063_GPIO8_PIN_MASK 0x03 +#define DA9063_GPIO8_PIN_GPI_SYS_EN 0x00 +#define DA9063_GPIO8_PIN_GPI 0x01 +#define DA9063_GPIO8_PIN_GPO_PSS 0x02 +#define DA9063_GPIO8_PIN_GPO 0x03 +#define DA9063_GPIO8_TYPE 0x04 +#define DA9063_GPIO8_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO8_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO8_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO8_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO8_NO_WAKEUP 0x08 +#define DA9063_GPIO9_PIN_MASK 0x30 +#define DA9063_GPIO9_PIN_GPI_PWR_EN 0x00 +#define DA9063_GPIO9_PIN_GPI 0x10 +#define DA9063_GPIO9_PIN_GPO_PSS 0x20 +#define DA9063_GPIO9_PIN_GPO 0x30 +#define DA9063_GPIO9_TYPE 0x40 +#define DA9063_GPIO9_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO9_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO9_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO9_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO9_NO_WAKEUP 0x80 + +/* DA9063_REG_GPIO_10_11 (addr=0x1A) */ +#define DA9063_GPIO10_PIN_MASK 0x03 +#define DA9063_GPIO10_PIN_GPI_PWR1_EN 0x00 +#define DA9063_GPIO10_PIN_GPI 0x01 +#define DA9063_GPIO10_PIN_GPO_OD 0x02 +#define DA9063_GPIO10_PIN_GPO 0x03 +#define DA9063_GPIO10_TYPE 0x04 +#define DA9063_GPIO10_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO10_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO10_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO10_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO10_NO_WAKEUP 0x08 +#define DA9063_GPIO11_PIN_MASK 0x30 +#define DA9063_GPIO11_PIN_GPO_OD 0x00 +#define DA9063_GPIO11_PIN_GPI 0x10 +#define DA9063_GPIO11_PIN_GPO_PSS 0x20 +#define DA9063_GPIO11_PIN_GPO 0x30 +#define DA9063_GPIO11_TYPE 0x40 +#define DA9063_GPIO11_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO11_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO11_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO11_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO11_NO_WAKEUP 0x80 + +/* DA9063_REG_GPIO_12_13 (addr=0x1B) */ +#define DA9063_GPIO12_PIN_MASK 0x03 +#define DA9063_GPIO12_PIN_NVDDFLT_OUT 0x00 +#define DA9063_GPIO12_PIN_GPI 0x01 +#define DA9063_GPIO12_PIN_VSYSMON_OUT 0x02 +#define DA9063_GPIO12_PIN_GPO 0x03 +#define DA9063_GPIO12_TYPE 0x04 +#define DA9063_GPIO12_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO12_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO12_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO12_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO12_NO_WAKEUP 0x08 +#define DA9063_GPIO13_PIN_MASK 0x30 +#define DA9063_GPIO13_PIN_GPFB1_OUT 0x00 +#define DA9063_GPIO13_PIN_GPI 0x10 +#define DA9063_GPIO13_PIN_GPFB1_OUTOD 0x20 +#define DA9063_GPIO13_PIN_GPO 0x30 +#define DA9063_GPIO13_TYPE 0x40 +#define DA9063_GPIO13_TYPE_GPFB1_OUT 0x00 +#define DA9063_GPIO13_TYPE_GPI 0x00 +#define DA9063_GPIO13_TYPE_GPFB1_OUTOD 0x04 +#define DA9063_GPIO13_TYPE_GPO 0x04 +#define DA9063_GPIO13_NO_WAKEUP 0x80 + +/* DA9063_REG_GPIO_14_15 (addr=0x1C) */ +#define DA9063_GPIO14_PIN_MASK 0x03 +#define DA9063_GPIO14_PIN_GPO_OD 0x00 +#define DA9063_GPIO14_PIN_GPI 0x01 +#define DA9063_GPIO14_PIN_HS2DATA 0x02 +#define DA9063_GPIO14_PIN_GPO 0x03 +#define DA9063_GPIO14_TYPE 0x04 +#define DA9063_GPIO14_TYPE_GPI_ACT_LOW 0x00 +#define DA9063_GPIO14_TYPE_GPO_VDD_IO1 0x00 +#define DA9063_GPIO14_TYPE_GPI_ACT_HIGH 0x04 +#define DA9063_GPIO14_TYPE_GPO_VDD_IO2 0x04 +#define DA9063_GPIO14_NO_WAKEUP 0x08 +#define DA9063_GPIO15_PIN_MASK 0x30 +#define DA9063_GPIO15_PIN_GPO_OD 0x00 +#define DA9063_GPIO15_PIN_GPI 0x10 +#define DA9063_GPIO15_PIN_GPO 0x30 +#define DA9063_GPIO15_TYPE 0x40 +#define DA9063_GPIO15_TYPE_GPFB1_OUT 0x00 +#define DA9063_GPIO15_TYPE_GPI 0x00 +#define DA9063_GPIO15_TYPE_GPFB1_OUTOD 0x04 +#define DA9063_GPIO15_TYPE_GPO 0x04 +#define DA9063_GPIO15_NO_WAKEUP 0x80 + +/* DA9063_REG_GPIO_MODE_0_7 (addr=0x1D) */ +#define DA9063_GPIO0_MODE 0x01 +#define DA9063_GPIO1_MODE 0x02 +#define DA9063_GPIO2_MODE 0x04 +#define DA9063_GPIO3_MODE 0x08 +#define DA9063_GPIO4_MODE 0x10 +#define DA9063_GPIO5_MODE 0x20 +#define DA9063_GPIO6_MODE 0x40 +#define DA9063_GPIO7_MODE 0x80 + +/* DA9063_REG_GPIO_MODE_8_15 (addr=0x1E) */ +#define DA9063_GPIO8_MODE 0x01 +#define DA9063_GPIO9_MODE 0x02 +#define DA9063_GPIO10_MODE 0x04 +#define DA9063_GPIO11_MODE 0x08 +#define DA9063_GPIO11_MODE_LED_ACT_HIGH 0x00 +#define DA9063_GPIO11_MODE_LED_ACT_LOW 0x08 +#define DA9063_GPIO12_MODE 0x10 +#define DA9063_GPIO13_MODE 0x20 +#define DA9063_GPIO14_MODE 0x40 +#define DA9063_GPIO14_MODE_LED_ACT_HIGH 0x00 +#define DA9063_GPIO14_MODE_LED_ACT_LOW 0x40 +#define DA9063_GPIO15_MODE 0x80 +#define DA9063_GPIO15_MODE_LED_ACT_HIGH 0x00 +#define DA9063_GPIO15_MODE_LED_ACT_LOW 0x80 + +/* DA9063_REG_SWITCH_CONT (addr=0x1F) */ +#define DA9063_CORE_SW_GPI_MASK 0x03 +#define DA9063_CORE_SW_GPI_OFF 0x00 +#define DA9063_CORE_SW_GPI_GPIO1 0x01 +#define DA9063_CORE_SW_GPI_GPIO2 0x02 +#define DA9063_CORE_SW_GPI_GPIO13 0x03 +#define DA9063_PERI_SW_GPI_MASK 0x0C +#define DA9063_PERI_SW_GPI_OFF 0x00 +#define DA9063_PERI_SW_GPI_GPIO1 0x04 +#define DA9063_PERI_SW_GPI_GPIO2 0x08 +#define DA9063_PERI_SW_GPI_GPIO13 0x0C +#define DA9063_SWITCH_SR_MASK 0x30 +#define DA9063_SWITCH_SR_1MV 0x00 +#define DA9063_SWITCH_SR_5MV 0x10 +#define DA9063_SWITCH_SR_10MV 0x20 +#define DA9063_SWITCH_SR_50MV 0x30 +#define DA9063_SWITCH_SR_DIS 0x40 +#define DA9063_CP_EN_MODE 0x80 + +/* DA9063_REGL_Bxxxx_CONT common bits (addr=0x20-0x25) */ +#define DA9063_BUCK_EN 0x01 +#define DA9063_BUCK_GPI_MASK 0x06 +#define DA9063_BUCK_GPI_OFF 0x00 +#define DA9063_BUCK_GPI_GPIO1 0x02 +#define DA9063_BUCK_GPI_GPIO2 0x04 +#define DA9063_BUCK_GPI_GPIO13 0x06 +#define DA9063_BUCK_CONF 0x08 +#define DA9063_VBUCK_GPI_MASK 0x60 +#define DA9063_VBUCK_GPI_OFF 0x00 +#define DA9063_VBUCK_GPI_GPIO1 0x20 +#define DA9063_VBUCK_GPI_GPIO2 0x40 +#define DA9063_VBUCK_GPI_GPIO13 0x60 + +/* DA9063_REG_BCORE1_CONT specific bits (addr=0x21) */ +#define DA9063_CORE_SW_EN 0x10 +#define DA9063_CORE_SW_CONF 0x80 + +/* DA9063_REG_BPERI_CONT specific bits (addr=0x25) */ +#define DA9063_PERI_SW_EN 0x10 +#define DA9063_PERI_SW_CONF 0x80 + +/* DA9063_REG_LDOx_CONT common bits (addr=0x26-0x30) */ +#define DA9063_LDO_EN 0x01 +#define DA9063_LDO_GPI_MASK 0x06 +#define DA9063_LDO_GPI_OFF 0x00 +#define DA9063_LDO_GPI_GPIO1 0x02 +#define DA9063_LDO_GPI_GPIO2 0x04 +#define DA9063_LDO_GPI_GPIO13 0x06 +#define DA9063_LDO_PD_DIS 0x08 +#define DA9063_VLDO_GPI_MASK 0x60 +#define DA9063_VLDO_GPI_OFF 0x00 +#define DA9063_VLDO_GPI_GPIO1 0x20 +#define DA9063_VLDO_GPI_GPIO2 0x40 +#define DA9063_VLDO_GPI_GPIO13 0x60 +#define DA9063_LDO_CONF 0x80 + +/* DA9063_REG_LDO5_CONT specific bits (addr=0x2A) */ +#define DA9063_VLDO5_SEL 0x10 + +/* DA9063_REG_LDO6_CONT specific bits (addr=0x2B) */ +#define DA9063_VLDO6_SEL 0x10 + +/* DA9063_REG_LDO7_CONT specific bits (addr=0x2C) */ +#define DA9063_VLDO7_SEL 0x10 + +/* DA9063_REG_LDO8_CONT specific bits (addr=0x2D) */ +#define DA9063_VLDO8_SEL 0x10 + +/* DA9063_REG_LDO9_CONT specific bits (addr=0x2E) */ +#define DA9063_VLDO9_SEL 0x10 + +/* DA9063_REG_LDO10_CONT specific bits (addr=0x2F) */ +#define DA9063_VLDO10_SEL 0x10 + +/* DA9063_REG_LDO11_CONT specific bits (addr=0x30) */ +#define DA9063_VLDO11_SEL 0x10 + +/* DA9063_REG_VIB (addr=0x31) */ +#define DA9063_VIB_SET_MASK 0x3F +#define DA9063_VIB_SET_OFF 0 +#define DA9063_VIB_SET_MAX 0x3F + +/* DA9063_REG_DVC_1 (addr=0x32) */ +#define DA9063_VBCORE1_SEL 0x01 +#define DA9063_VBCORE2_SEL 0x02 +#define DA9063_VBPRO_SEL 0x04 +#define DA9063_VBMEM_SEL 0x08 +#define DA9063_VBPERI_SEL 0x10 +#define DA9063_VLDO1_SEL 0x20 +#define DA9063_VLDO2_SEL 0x40 +#define DA9063_VLDO3_SEL 0x80 + +/* DA9063_REG_DVC_2 (addr=0x33) */ +#define DA9063_VBIO_SEL 0x01 +#define DA9063_VLDO4_SEL 0x80 + +/* DA9063_REG_ADC_MAN (addr=0x34) */ +#define DA9063_ADC_MUX_MASK 0x0F +#define DA9063_ADC_MUX_VSYS 0x00 +#define DA9063_ADC_MUX_ADCIN1 0x01 +#define DA9063_ADC_MUX_ADCIN2 0x02 +#define DA9063_ADC_MUX_ADCIN3 0x03 +#define DA9063_ADC_MUX_T_SENSE 0x04 +#define DA9063_ADC_MUX_VBBAT 0x05 +#define DA9063_ADC_MUX_LDO_G1 0x08 +#define DA9063_ADC_MUX_LDO_G2 0x09 +#define DA9063_ADC_MUX_LDO_G3 0x0A +#define DA9063_ADC_MAN 0x10 +#define DA9063_ADC_MODE 0x20 + +/* DA9063_REG_ADC_CONT (addr=0x35) */ +#define DA9063_ADC_AUTO_VSYS_EN 0x01 +#define DA9063_ADC_AUTO_AD1_EN 0x02 +#define DA9063_ADC_AUTO_AD2_EN 0x04 +#define DA9063_ADC_AUTO_AD3_EN 0x08 +#define DA9063_ADC_AD1_ISRC_EN 0x10 +#define DA9063_ADC_AD2_ISRC_EN 0x20 +#define DA9063_ADC_AD3_ISRC_EN 0x40 +#define DA9063_COMP1V2_EN 0x80 + +/* DA9063_REG_VSYS_MON (addr=0x36) */ +#define DA9063_VSYS_VAL_MASK 0xFF +#define DA9063_VSYS_VAL_BASE 0x00 + +/* DA9063_REG_ADC_RES_L (addr=0x37) */ +#define DA9063_ADC_RES_L_BITS 2 +#define DA9063_ADC_RES_L_MASK 0xC0 + +/* DA9063_REG_ADC_RES_H (addr=0x38) */ +#define DA9063_ADC_RES_M_BITS 8 +#define DA9063_ADC_RES_M_MASK 0xFF + +/* DA9063_REG_(xxx_RES/ADC_RES_H) (addr=0x39-0x3F) */ +#define DA9063_ADC_VAL_MASK 0xFF + +/* DA9063_REG_COUNT_S (addr=0x40) */ +#define DA9063_RTC_READ 0x80 +#define DA9063_COUNT_SEC_MASK 0x3F + +/* DA9063_REG_COUNT_MI (addr=0x41) */ +#define DA9063_COUNT_MIN_MASK 0x3F + +/* DA9063_REG_COUNT_H (addr=0x42) */ +#define DA9063_COUNT_HOUR_MASK 0x1F + +/* DA9063_REG_COUNT_D (addr=0x43) */ +#define DA9063_COUNT_DAY_MASK 0x1F + +/* DA9063_REG_COUNT_MO (addr=0x44) */ +#define DA9063_COUNT_MONTH_MASK 0x0F + +/* DA9063_REG_COUNT_Y (addr=0x45) */ +#define DA9063_COUNT_YEAR_MASK 0x3F +#define DA9063_MONITOR 0x40 + +/* DA9063_REG_ALARM_MI (addr=0x46) */ +#define DA9063_ALARM_STATUS_ALARM 0x80 +#define DA9063_ALARM_STATUS_TICK 0x40 +#define DA9063_ALARM_MIN_MASK 0x3F + +/* DA9063_REG_ALARM_H (addr=0x47) */ +#define DA9063_ALARM_HOUR_MASK 0x1F + +/* DA9063_REG_ALARM_D (addr=0x48) */ +#define DA9063_ALARM_DAY_MASK 0x1F + +/* DA9063_REG_ALARM_MO (addr=0x49) */ +#define DA9063_TICK_WAKE 0x20 +#define DA9063_TICK_TYPE 0x10 +#define DA9063_TICK_TYPE_SEC 0x00 +#define DA9063_TICK_TYPE_MIN 0x10 +#define DA9063_ALARM_MONTH_MASK 0x0F + +/* DA9063_REG_ALARM_Y (addr=0x4A) */ +#define DA9063_TICK_ON 0x80 +#define DA9063_ALARM_ON 0x40 +#define DA9063_ALARM_YEAR_MASK 0x3F + +/* DA9063_REG_WAIT (addr=0x97)*/ +#define DA9063_REG_WAIT_TIME_MASK 0xF +#define DA9063_WAIT_TIME_0_US 0x0 +#define DA9063_WAIT_TIME_512_US 0x1 +#define DA9063_WAIT_TIME_1_MS 0x2 +#define DA9063_WAIT_TIME_2_MS 0x3 +#define DA9063_WAIT_TIME_4_1_MS 0x4 +#define DA9063_WAIT_TIME_8_2_MS 0x5 +#define DA9063_WAIT_TIME_16_4_MS 0x6 +#define DA9063_WAIT_TIME_32_8_MS 0x7 +#define DA9063_WAIT_TIME_65_5_MS 0x8 +#define DA9063_WAIT_TIME_128_MS 0x9 +#define DA9063_WAIT_TIME_256_MS 0xA +#define DA9063_WAIT_TIME_512_MS 0xB +#define DA9063_WAIT_TIME_1_S 0xC +#define DA9063_WAIT_TIME_2_1_S 0xD + +/* DA9063_REG_EN_32K (addr=0x98)*/ +#define DA9063_STABILIZ_TIME_MASK 0x7 +#define DA9063_CRYSTAL 0x08 +#define DA9063_DELAY_MODE 0x10 +#define DA9063_OUT_CLOCK 0x20 +#define DA9063_RTC_CLOCK 0x40 +#define DA9063_OUT_32K_EN 0x80 + +/* DA9063_REG_CHIP_VARIANT */ +#define DA9063_CHIP_VARIANT_SHIFT 4 + +/* DA9063_REG_BUCK_ILIM_A (addr=0x9A) */ +#define DA9063_BIO_ILIM_MASK 0x0F +#define DA9063_BMEM_ILIM_MASK 0xF0 + +/* DA9063_REG_BUCK_ILIM_B (addr=0x9B) */ +#define DA9063_BPRO_ILIM_MASK 0x0F +#define DA9063_BPERI_ILIM_MASK 0xF0 + +/* DA9063_REG_BUCK_ILIM_C (addr=0x9C) */ +#define DA9063_BCORE1_ILIM_MASK 0x0F +#define DA9063_BCORE2_ILIM_MASK 0xF0 + +/* DA9063_REG_Bxxxx_CFG common bits (addr=0x9D-0xA2) */ +#define DA9063_BUCK_FB_MASK 0x07 +#define DA9063_BUCK_PD_DIS_SHIFT 5 +#define DA9063_BUCK_MODE_MASK 0xC0 +#define DA9063_BUCK_MODE_MANUAL 0x00 +#define DA9063_BUCK_MODE_SLEEP 0x40 +#define DA9063_BUCK_MODE_SYNC 0x80 +#define DA9063_BUCK_MODE_AUTO 0xC0 + +/* DA9063_REG_BPRO_CFG (addr=0x9F) */ +#define DA9063_BPRO_VTTR_EN 0x08 +#define DA9063_BPRO_VTT_EN 0x10 + +/* DA9063_REG_VBxxxx_A/B (addr=0xA3-0xA8, 0xB4-0xB9) */ +#define DA9063_VBUCK_MASK 0x7F +#define DA9063_VBUCK_BIAS 0 +#define DA9063_BUCK_SL 0x80 + +/* DA9063_REG_VLDOx_A/B (addr=0xA9-0x3, 0xBA-0xC4) */ +#define DA9063_LDO_SL 0x80 + +/* DA9063_REG_VLDO1_A/B (addr=0xA9, 0xBA) */ +#define DA9063_VLDO1_MASK 0x3F +#define DA9063_VLDO1_BIAS 0 + +/* DA9063_REG_VLDO2_A/B (addr=0xAA, 0xBB) */ +#define DA9063_VLDO2_MASK 0x3F +#define DA9063_VLDO2_BIAS 0 + +/* DA9063_REG_VLDO3_A/B (addr=0xAB, 0xBC) */ +#define DA9063_VLDO3_MASK 0x7F +#define DA9063_VLDO3_BIAS 0 + +/* DA9063_REG_VLDO4_A/B (addr=0xAC, 0xBD) */ +#define DA9063_VLDO4_MASK 0x7F +#define DA9063_VLDO4_BIAS 0 + +/* DA9063_REG_VLDO5_A/B (addr=0xAD, 0xBE) */ +#define DA9063_VLDO5_MASK 0x3F +#define DA9063_VLDO5_BIAS 2 + +/* DA9063_REG_VLDO6_A/B (addr=0xAE, 0xBF) */ +#define DA9063_VLDO6_MASK 0x3F +#define DA9063_VLDO6_BIAS 2 + +/* DA9063_REG_VLDO7_A/B (addr=0xAF, 0xC0) */ +#define DA9063_VLDO7_MASK 0x3F +#define DA9063_VLDO7_BIAS 2 + +/* DA9063_REG_VLDO8_A/B (addr=0xB0, 0xC1) */ +#define DA9063_VLDO8_MASK 0x3F +#define DA9063_VLDO8_BIAS 2 + +/* DA9063_REG_VLDO9_A/B (addr=0xB1, 0xC2) */ +#define DA9063_VLDO9_MASK 0x3F +#define DA9063_VLDO9_BIAS 3 + +/* DA9063_REG_VLDO10_A/B (addr=0xB2, 0xC3) */ +#define DA9063_VLDO10_MASK 0x3F +#define DA9063_VLDO10_BIAS 2 + +/* DA9063_REG_VLDO11_A/B (addr=0xB3, 0xC4) */ +#define DA9063_VLDO11_MASK 0x3F +#define DA9063_VLDO11_BIAS 2 + +/* DA9063_REG_GPO11_LED (addr=0xC6) */ +/* DA9063_REG_GPO14_LED (addr=0xC7) */ +/* DA9063_REG_GPO15_LED (addr=0xC8) */ +#define DA9063_GPIO_DIM 0x80 +#define DA9063_GPIO_PWM_MASK 0x7F + +/* DA9063_REG_CONFIG_H (addr=0x10D) */ +#define DA9063_PWM_CLK_MASK 0x01 +#define DA9063_PWM_CLK_PWM2MHZ 0x00 +#define DA9063_PWM_CLK_PWM1MHZ 0x01 +#define DA9063_LDO8_MODE_MASK 0x02 +#define DA9063_LDO8_MODE_LDO 0 +#define DA9063_LDO8_MODE_VIBR 0x02 +#define DA9063_MERGE_SENSE_MASK 0x04 +#define DA9063_MERGE_SENSE_GP_FB2 0x00 +#define DA9063_MERGE_SENSE_GPIO4 0x04 +#define DA9063_BCORE_MERGE 0x08 +#define DA9063_BPRO_OD 0x10 +#define DA9063_BCORE2_OD 0x20 +#define DA9063_BCORE1_OD 0x40 +#define DA9063_BUCK_MERGE 0x80 + +/* DA9063_REG_CONFIG_I (addr=0x10E) */ +#define DA9063_NONKEY_PIN_MASK 0x03 +#define DA9063_NONKEY_PIN_PORT 0x00 +#define DA9063_NONKEY_PIN_SWDOWN 0x01 +#define DA9063_NONKEY_PIN_AUTODOWN 0x02 +#define DA9063_NONKEY_PIN_AUTOFLPRT 0x03 + +/* DA9063_REG_MON_REG_5 (addr=0x116) */ +#define DA9063_MON_A8_IDX_MASK 0x07 +#define DA9063_MON_A8_IDX_NONE 0x00 +#define DA9063_MON_A8_IDX_BCORE1 0x01 +#define DA9063_MON_A8_IDX_BCORE2 0x02 +#define DA9063_MON_A8_IDX_BPRO 0x03 +#define DA9063_MON_A8_IDX_LDO3 0x04 +#define DA9063_MON_A8_IDX_LDO4 0x05 +#define DA9063_MON_A8_IDX_LDO11 0x06 +#define DA9063_MON_A9_IDX_MASK 0x70 +#define DA9063_MON_A9_IDX_NONE 0x00 +#define DA9063_MON_A9_IDX_BIO 0x01 +#define DA9063_MON_A9_IDX_BMEM 0x02 +#define DA9063_MON_A9_IDX_BPERI 0x03 +#define DA9063_MON_A9_IDX_LDO1 0x04 +#define DA9063_MON_A9_IDX_LDO2 0x05 +#define DA9063_MON_A9_IDX_LDO5 0x06 + +/* DA9063_REG_MON_REG_6 (addr=0x117) */ +#define DA9063_MON_A10_IDX_MASK 0x07 +#define DA9063_MON_A10_IDX_NONE 0x00 +#define DA9063_MON_A10_IDX_LDO6 0x01 +#define DA9063_MON_A10_IDX_LDO7 0x02 +#define DA9063_MON_A10_IDX_LDO8 0x03 +#define DA9063_MON_A10_IDX_LDO9 0x04 +#define DA9063_MON_A10_IDX_LDO10 0x05 + +#endif /* _DA9063_REG_H */ -- cgit v1.2.3-71-gd317 From 8e685483b0ba17fe08cfc36fb86b3688a24b2090 Mon Sep 17 00:00:00 2001 From: Krystian Garbaciak Date: Mon, 29 Jul 2013 19:00:43 +0200 Subject: mfd: da9063: Add Dialog DA9063 core driver This is MFD module providing access to registers and interrupts of DA906x series PMIC. It is used by other functional modules, registered as MFD cells. Driver uses regmap with paging to access extended register list. Register map is divided into two pages, where the second page is used during initialisation. This module provides support to following functional cells: - Regulators - RTC - HWMON - OnKey (power key misc input device) - Vibration (force-feedback input device) - Watchdog - LEDs Signed-off-by: Krystian Garbaciak Signed-off-by: Philipp Zabel Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 12 +++ drivers/mfd/Makefile | 3 + drivers/mfd/da9063-core.c | 129 +++++++++++++++++++++++++++ drivers/mfd/da9063-i2c.c | 182 +++++++++++++++++++++++++++++++++++++++ include/linux/mfd/da9063/core.h | 55 ++++++++++++ include/linux/mfd/da9063/pdata.h | 111 ++++++++++++++++++++++++ 6 files changed, 492 insertions(+) create mode 100644 drivers/mfd/da9063-core.c create mode 100644 drivers/mfd/da9063-i2c.c create mode 100644 include/linux/mfd/da9063/core.h create mode 100644 include/linux/mfd/da9063/pdata.h (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 633ee43efae6..e0e46f50f95d 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -139,6 +139,18 @@ config MFD_DA9055 This driver can be built as a module. If built as a module it will be called "da9055" +config MFD_DA9063 + bool "Dialog Semiconductor DA9063 PMIC Support" + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + depends on I2C=y && GENERIC_HARDIRQS + help + Say yes here for support for the Dialog Semiconductor DA9063 PMIC. + This includes the I2C driver and core APIs. + Additional drivers must be enabled in order to use the functionality + of the device. + config MFD_MC13783 tristate diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 3c90051ffa5a..0bb84d59f3fa 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -107,6 +107,9 @@ obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o da9055-objs := da9055-core.o da9055-i2c.o obj-$(CONFIG_MFD_DA9055) += da9055.o +da9063-objs := da9063-core.o da9063-i2c.o +obj-$(CONFIG_MFD_DA9063) += da9063.o + obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o obj-$(CONFIG_MFD_MAX77693) += max77693.o max77693-irq.o obj-$(CONFIG_MFD_MAX8907) += max8907.o diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c new file mode 100644 index 000000000000..29f3ed1f076c --- /dev/null +++ b/drivers/mfd/da9063-core.c @@ -0,0 +1,129 @@ +/* + * da9063-core.c: Device access for Dialog DA9063 modules + * + * Copyright 2012 Dialog Semiconductors Ltd. + * Copyright 2013 Philipp Zabel, Pengutronix + * + * Author: Krystian Garbaciak , + * Michal Hajduk + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + + +static struct mfd_cell da9063_devs[] = { + { + .name = DA9063_DRVNAME_REGULATORS, + }, + { + .name = DA9063_DRVNAME_LEDS, + }, + { + .name = DA9063_DRVNAME_WATCHDOG, + }, + { + .name = DA9063_DRVNAME_HWMON, + }, + { + .name = DA9063_DRVNAME_ONKEY, + }, + { + .name = DA9063_DRVNAME_RTC, + }, + { + .name = DA9063_DRVNAME_VIBRATION, + }, +}; + +int da9063_device_init(struct da9063 *da9063, unsigned int irq) +{ + struct da9063_pdata *pdata = da9063->dev->platform_data; + int model, revision; + int ret; + + if (pdata) { + da9063->flags = pdata->flags; + da9063->irq_base = pdata->irq_base; + } else { + da9063->flags = 0; + da9063->irq_base = 0; + } + da9063->chip_irq = irq; + + if (pdata && pdata->init != NULL) { + ret = pdata->init(da9063); + if (ret != 0) { + dev_err(da9063->dev, + "Platform initialization failed.\n"); + return ret; + } + } + + ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_ID, &model); + if (ret < 0) { + dev_err(da9063->dev, "Cannot read chip model id.\n"); + return -EIO; + } + if (model != PMIC_DA9063) { + dev_err(da9063->dev, "Invalid chip model id: 0x%02x\n", model); + return -ENODEV; + } + + ret = regmap_read(da9063->regmap, DA9063_REG_CHIP_VARIANT, &revision); + if (ret < 0) { + dev_err(da9063->dev, "Cannot read chip revision id.\n"); + return -EIO; + } + revision >>= DA9063_CHIP_VARIANT_SHIFT; + if (revision != 3) { + dev_err(da9063->dev, "Unknown chip revision: %d\n", revision); + return -ENODEV; + } + + da9063->model = model; + da9063->revision = revision; + + dev_info(da9063->dev, + "Device detected (model-ID: 0x%02X rev-ID: 0x%02X)\n", + model, revision); + + ret = mfd_add_devices(da9063->dev, -1, da9063_devs, + ARRAY_SIZE(da9063_devs), NULL, da9063->irq_base, + NULL); + if (ret) + dev_err(da9063->dev, "Cannot add MFD cells\n"); + + return ret; +} + +void da9063_device_exit(struct da9063 *da9063) +{ + mfd_remove_devices(da9063->dev); +} + +MODULE_DESCRIPTION("PMIC driver for Dialog DA9063"); +MODULE_AUTHOR("Krystian Garbaciak , Michal Hajduk "); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/da9063-i2c.c b/drivers/mfd/da9063-i2c.c new file mode 100644 index 000000000000..8db5c805c64f --- /dev/null +++ b/drivers/mfd/da9063-i2c.c @@ -0,0 +1,182 @@ +/* da9063-i2c.c: Interrupt support for Dialog DA9063 + * + * Copyright 2012 Dialog Semiconductor Ltd. + * Copyright 2013 Philipp Zabel, Pengutronix + * + * Author: Krystian Garbaciak + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static const struct regmap_range da9063_readable_ranges[] = { + { + .range_min = DA9063_REG_PAGE_CON, + .range_max = DA9063_REG_SECOND_D, + }, { + .range_min = DA9063_REG_SEQ, + .range_max = DA9063_REG_ID_32_31, + }, { + .range_min = DA9063_REG_SEQ_A, + .range_max = DA9063_REG_AUTO3_LOW, + }, { + .range_min = DA9063_REG_T_OFFSET, + .range_max = DA9063_REG_GP_ID_19, + }, { + .range_min = DA9063_REG_CHIP_ID, + .range_max = DA9063_REG_CHIP_VARIANT, + }, +}; + +static const struct regmap_range da9063_writeable_ranges[] = { + { + .range_min = DA9063_REG_PAGE_CON, + .range_max = DA9063_REG_PAGE_CON, + }, { + .range_min = DA9063_REG_FAULT_LOG, + .range_max = DA9063_REG_VSYS_MON, + }, { + .range_min = DA9063_REG_COUNT_S, + .range_max = DA9063_REG_ALARM_Y, + }, { + .range_min = DA9063_REG_SEQ, + .range_max = DA9063_REG_ID_32_31, + }, { + .range_min = DA9063_REG_SEQ_A, + .range_max = DA9063_REG_AUTO3_LOW, + }, { + .range_min = DA9063_REG_CONFIG_I, + .range_max = DA9063_REG_MON_REG_4, + }, { + .range_min = DA9063_REG_GP_ID_0, + .range_max = DA9063_REG_GP_ID_19, + }, +}; + +static const struct regmap_range da9063_volatile_ranges[] = { + { + .range_min = DA9063_REG_STATUS_A, + .range_max = DA9063_REG_EVENT_D, + }, { + .range_min = DA9063_REG_CONTROL_F, + .range_max = DA9063_REG_CONTROL_F, + }, { + .range_min = DA9063_REG_ADC_MAN, + .range_max = DA9063_REG_ADC_MAN, + }, { + .range_min = DA9063_REG_ADC_RES_L, + .range_max = DA9063_REG_SECOND_D, + }, { + .range_min = DA9063_REG_MON_REG_5, + .range_max = DA9063_REG_MON_REG_6, + }, +}; + +static const struct regmap_access_table da9063_readable_table = { + .yes_ranges = da9063_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9063_readable_ranges), +}; + +static const struct regmap_access_table da9063_writeable_table = { + .yes_ranges = da9063_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(da9063_writeable_ranges), +}; + +static const struct regmap_access_table da9063_volatile_table = { + .yes_ranges = da9063_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(da9063_volatile_ranges), +}; + +static const struct regmap_range_cfg da9063_range_cfg[] = { + { + .range_min = DA9063_REG_PAGE_CON, + .range_max = DA9063_REG_CHIP_VARIANT, + .selector_reg = DA9063_REG_PAGE_CON, + .selector_mask = 1 << DA9063_I2C_PAGE_SEL_SHIFT, + .selector_shift = DA9063_I2C_PAGE_SEL_SHIFT, + .window_start = 0, + .window_len = 256, + } +}; + +static struct regmap_config da9063_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .ranges = da9063_range_cfg, + .num_ranges = ARRAY_SIZE(da9063_range_cfg), + .max_register = DA9063_REG_CHIP_VARIANT, + + .cache_type = REGCACHE_RBTREE, + + .rd_table = &da9063_readable_table, + .wr_table = &da9063_writeable_table, + .volatile_table = &da9063_volatile_table, +}; + +static int da9063_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct da9063 *da9063; + int ret; + + da9063 = devm_kzalloc(&i2c->dev, sizeof(struct da9063), GFP_KERNEL); + if (da9063 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, da9063); + da9063->dev = &i2c->dev; + da9063->chip_irq = i2c->irq; + + da9063->regmap = devm_regmap_init_i2c(i2c, &da9063_regmap_config); + if (IS_ERR(da9063->regmap)) { + ret = PTR_ERR(da9063->regmap); + dev_err(da9063->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + return da9063_device_init(da9063, i2c->irq); +} + +static int da9063_i2c_remove(struct i2c_client *i2c) +{ + struct da9063 *da9063 = i2c_get_clientdata(i2c); + + da9063_device_exit(da9063); + + return 0; +} + +static const struct i2c_device_id da9063_i2c_id[] = { + {"da9063", PMIC_DA9063}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, da9063_i2c_id); + +static struct i2c_driver da9063_i2c_driver = { + .driver = { + .name = "da9063", + .owner = THIS_MODULE, + }, + .probe = da9063_i2c_probe, + .remove = da9063_i2c_remove, + .id_table = da9063_i2c_id, +}; + +module_i2c_driver(da9063_i2c_driver); diff --git a/include/linux/mfd/da9063/core.h b/include/linux/mfd/da9063/core.h new file mode 100644 index 000000000000..ec2fd2aa24ae --- /dev/null +++ b/include/linux/mfd/da9063/core.h @@ -0,0 +1,55 @@ +/* + * Definitions for DA9063 MFD driver + * + * Copyright 2012 Dialog Semiconductor Ltd. + * + * Author: Michal Hajduk + * Krystian Garbaciak + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __MFD_DA9063_CORE_H__ +#define __MFD_DA9063_CORE_H__ + +#include +#include + +/* DA9063 modules */ +#define DA9063_DRVNAME_CORE "da9063-core" +#define DA9063_DRVNAME_REGULATORS "da9063-regulators" +#define DA9063_DRVNAME_LEDS "da9063-leds" +#define DA9063_DRVNAME_WATCHDOG "da9063-watchdog" +#define DA9063_DRVNAME_HWMON "da9063-hwmon" +#define DA9063_DRVNAME_ONKEY "da9063-onkey" +#define DA9063_DRVNAME_RTC "da9063-rtc" +#define DA9063_DRVNAME_VIBRATION "da9063-vibration" + +enum da9063_models { + PMIC_DA9063 = 0x61, +}; + +struct da9063 { + /* Device */ + struct device *dev; + unsigned short model; + unsigned short revision; + unsigned int flags; + + /* Control interface */ + struct regmap *regmap; + + /* Interrupts */ + int chip_irq; + unsigned int irq_base; +}; + +int da9063_device_init(struct da9063 *da9063, unsigned int irq); + +void da9063_device_exit(struct da9063 *da9063); + +#endif /* __MFD_DA9063_CORE_H__ */ diff --git a/include/linux/mfd/da9063/pdata.h b/include/linux/mfd/da9063/pdata.h new file mode 100644 index 000000000000..95c8742215a7 --- /dev/null +++ b/include/linux/mfd/da9063/pdata.h @@ -0,0 +1,111 @@ +/* + * Platform configuration options for DA9063 + * + * Copyright 2012 Dialog Semiconductor Ltd. + * + * Author: Michal Hajduk + * Author: Krystian Garbaciak + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __MFD_DA9063_PDATA_H__ +#define __MFD_DA9063_PDATA_H__ + +#include + +/* + * Regulator configuration + */ +/* DA9063 regulator IDs */ +enum { + /* BUCKs */ + DA9063_ID_BCORE1, + DA9063_ID_BCORE2, + DA9063_ID_BPRO, + DA9063_ID_BMEM, + DA9063_ID_BIO, + DA9063_ID_BPERI, + + /* BCORE1 and BCORE2 in merged mode */ + DA9063_ID_BCORES_MERGED, + /* BMEM and BIO in merged mode */ + DA9063_ID_BMEM_BIO_MERGED, + /* When two BUCKs are merged, they cannot be reused separately */ + + /* LDOs */ + DA9063_ID_LDO1, + DA9063_ID_LDO2, + DA9063_ID_LDO3, + DA9063_ID_LDO4, + DA9063_ID_LDO5, + DA9063_ID_LDO6, + DA9063_ID_LDO7, + DA9063_ID_LDO8, + DA9063_ID_LDO9, + DA9063_ID_LDO10, + DA9063_ID_LDO11, +}; + +/* Regulators platform data */ +struct da9063_regulator_data { + int id; + struct regulator_init_data *initdata; +}; + +struct da9063_regulators_pdata { + unsigned n_regulators; + struct da9063_regulator_data *regulator_data; +}; + + +/* + * RGB LED configuration + */ +/* LED IDs for flags in struct led_info. */ +enum { + DA9063_GPIO11_LED, + DA9063_GPIO14_LED, + DA9063_GPIO15_LED, + + DA9063_LED_NUM +}; +#define DA9063_LED_ID_MASK 0x3 + +/* LED polarity for flags in struct led_info. */ +#define DA9063_LED_HIGH_LEVEL_ACTIVE 0x0 +#define DA9063_LED_LOW_LEVEL_ACTIVE 0x4 + + +/* + * General PMIC configuration + */ +/* HWMON ADC channels configuration */ +#define DA9063_FLG_FORCE_IN0_MANUAL_MODE 0x0010 +#define DA9063_FLG_FORCE_IN0_AUTO_MODE 0x0020 +#define DA9063_FLG_FORCE_IN1_MANUAL_MODE 0x0040 +#define DA9063_FLG_FORCE_IN1_AUTO_MODE 0x0080 +#define DA9063_FLG_FORCE_IN2_MANUAL_MODE 0x0100 +#define DA9063_FLG_FORCE_IN2_AUTO_MODE 0x0200 +#define DA9063_FLG_FORCE_IN3_MANUAL_MODE 0x0400 +#define DA9063_FLG_FORCE_IN3_AUTO_MODE 0x0800 + +/* Disable register caching. */ +#define DA9063_FLG_NO_CACHE 0x0008 + +struct da9063; + +/* DA9063 platform data */ +struct da9063_pdata { + int (*init)(struct da9063 *da9063); + int irq_base; + unsigned flags; + struct da9063_regulators_pdata *regulators_pdata; + struct led_platform_data *leds_pdata; +}; + +#endif /* __MFD_DA9063_PDATA_H__ */ -- cgit v1.2.3-71-gd317 From a0e08b8606f3c0722b235a09b537264e5b14f748 Mon Sep 17 00:00:00 2001 From: Krystian Garbaciak Date: Mon, 29 Jul 2013 19:00:44 +0200 Subject: mfd: da9063: Add IRQ support This patch adds a regmap irqchip for DA9063 IRQs. It depends on git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git tags/regmap-irq-ack-mask Signed-off-by: Krystian Garbaciak Signed-off-by: Philipp Zabel Reviewed-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/Makefile | 2 +- drivers/mfd/da9063-core.c | 56 ++++++++++++ drivers/mfd/da9063-irq.c | 193 ++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/da9063/core.h | 38 ++++++++ 4 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/da9063-irq.c (limited to 'include') diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 0bb84d59f3fa..15b905c6553c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -107,7 +107,7 @@ obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o da9055-objs := da9055-core.o da9055-i2c.o obj-$(CONFIG_MFD_DA9055) += da9055.o -da9063-objs := da9063-core.o da9063-i2c.o +da9063-objs := da9063-core.o da9063-irq.o da9063-i2c.o obj-$(CONFIG_MFD_DA9063) += da9063.o obj-$(CONFIG_MFD_MAX77686) += max77686.o max77686-irq.o diff --git a/drivers/mfd/da9063-core.c b/drivers/mfd/da9063-core.c index 29f3ed1f076c..c9cf8d988406 100644 --- a/drivers/mfd/da9063-core.c +++ b/drivers/mfd/da9063-core.c @@ -34,9 +34,52 @@ #include +static struct resource da9063_regulators_resources[] = { + { + .name = "LDO_LIM", + .start = DA9063_IRQ_LDO_LIM, + .end = DA9063_IRQ_LDO_LIM, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource da9063_rtc_resources[] = { + { + .name = "ALARM", + .start = DA9063_IRQ_ALARM, + .end = DA9063_IRQ_ALARM, + .flags = IORESOURCE_IRQ, + }, + { + .name = "TICK", + .start = DA9063_IRQ_TICK, + .end = DA9063_IRQ_TICK, + .flags = IORESOURCE_IRQ, + } +}; + +static struct resource da9063_onkey_resources[] = { + { + .start = DA9063_IRQ_ONKEY, + .end = DA9063_IRQ_ONKEY, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource da9063_hwmon_resources[] = { + { + .start = DA9063_IRQ_ADC_RDY, + .end = DA9063_IRQ_ADC_RDY, + .flags = IORESOURCE_IRQ, + }, +}; + + static struct mfd_cell da9063_devs[] = { { .name = DA9063_DRVNAME_REGULATORS, + .num_resources = ARRAY_SIZE(da9063_regulators_resources), + .resources = da9063_regulators_resources, }, { .name = DA9063_DRVNAME_LEDS, @@ -46,12 +89,18 @@ static struct mfd_cell da9063_devs[] = { }, { .name = DA9063_DRVNAME_HWMON, + .num_resources = ARRAY_SIZE(da9063_hwmon_resources), + .resources = da9063_hwmon_resources, }, { .name = DA9063_DRVNAME_ONKEY, + .num_resources = ARRAY_SIZE(da9063_onkey_resources), + .resources = da9063_onkey_resources, }, { .name = DA9063_DRVNAME_RTC, + .num_resources = ARRAY_SIZE(da9063_rtc_resources), + .resources = da9063_rtc_resources, }, { .name = DA9063_DRVNAME_VIBRATION, @@ -110,6 +159,12 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq) "Device detected (model-ID: 0x%02X rev-ID: 0x%02X)\n", model, revision); + ret = da9063_irq_init(da9063); + if (ret) { + dev_err(da9063->dev, "Cannot initialize interrupts.\n"); + return ret; + } + ret = mfd_add_devices(da9063->dev, -1, da9063_devs, ARRAY_SIZE(da9063_devs), NULL, da9063->irq_base, NULL); @@ -122,6 +177,7 @@ int da9063_device_init(struct da9063 *da9063, unsigned int irq) void da9063_device_exit(struct da9063 *da9063) { mfd_remove_devices(da9063->dev); + da9063_irq_exit(da9063); } MODULE_DESCRIPTION("PMIC driver for Dialog DA9063"); diff --git a/drivers/mfd/da9063-irq.c b/drivers/mfd/da9063-irq.c new file mode 100644 index 000000000000..822922602ce9 --- /dev/null +++ b/drivers/mfd/da9063-irq.c @@ -0,0 +1,193 @@ +/* da9063-irq.c: Interrupts support for Dialog DA9063 + * + * Copyright 2012 Dialog Semiconductor Ltd. + * Copyright 2013 Philipp Zabel, Pengutronix + * + * Author: Michal Hajduk + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DA9063_REG_EVENT_A_OFFSET 0 +#define DA9063_REG_EVENT_B_OFFSET 1 +#define DA9063_REG_EVENT_C_OFFSET 2 +#define DA9063_REG_EVENT_D_OFFSET 3 +#define EVENTS_BUF_LEN 4 + +static const u8 mask_events_buf[] = { [0 ... (EVENTS_BUF_LEN - 1)] = ~0 }; + +struct da9063_irq_data { + u16 reg; + u8 mask; +}; + +static struct regmap_irq da9063_irqs[] = { + /* DA9063 event A register */ + [DA9063_IRQ_ONKEY] = { + .reg_offset = DA9063_REG_EVENT_A_OFFSET, + .mask = DA9063_M_ONKEY, + }, + [DA9063_IRQ_ALARM] = { + .reg_offset = DA9063_REG_EVENT_A_OFFSET, + .mask = DA9063_M_ALARM, + }, + [DA9063_IRQ_TICK] = { + .reg_offset = DA9063_REG_EVENT_A_OFFSET, + .mask = DA9063_M_TICK, + }, + [DA9063_IRQ_ADC_RDY] = { + .reg_offset = DA9063_REG_EVENT_A_OFFSET, + .mask = DA9063_M_ADC_RDY, + }, + [DA9063_IRQ_SEQ_RDY] = { + .reg_offset = DA9063_REG_EVENT_A_OFFSET, + .mask = DA9063_M_SEQ_RDY, + }, + /* DA9063 event B register */ + [DA9063_IRQ_WAKE] = { + .reg_offset = DA9063_REG_EVENT_B_OFFSET, + .mask = DA9063_M_WAKE, + }, + [DA9063_IRQ_TEMP] = { + .reg_offset = DA9063_REG_EVENT_B_OFFSET, + .mask = DA9063_M_TEMP, + }, + [DA9063_IRQ_COMP_1V2] = { + .reg_offset = DA9063_REG_EVENT_B_OFFSET, + .mask = DA9063_M_COMP_1V2, + }, + [DA9063_IRQ_LDO_LIM] = { + .reg_offset = DA9063_REG_EVENT_B_OFFSET, + .mask = DA9063_M_LDO_LIM, + }, + [DA9063_IRQ_REG_UVOV] = { + .reg_offset = DA9063_REG_EVENT_B_OFFSET, + .mask = DA9063_M_UVOV, + }, + [DA9063_IRQ_VDD_MON] = { + .reg_offset = DA9063_REG_EVENT_B_OFFSET, + .mask = DA9063_M_VDD_MON, + }, + [DA9063_IRQ_WARN] = { + .reg_offset = DA9063_REG_EVENT_B_OFFSET, + .mask = DA9063_M_VDD_WARN, + }, + /* DA9063 event C register */ + [DA9063_IRQ_GPI0] = { + .reg_offset = DA9063_REG_EVENT_C_OFFSET, + .mask = DA9063_M_GPI0, + }, + [DA9063_IRQ_GPI1] = { + .reg_offset = DA9063_REG_EVENT_C_OFFSET, + .mask = DA9063_M_GPI1, + }, + [DA9063_IRQ_GPI2] = { + .reg_offset = DA9063_REG_EVENT_C_OFFSET, + .mask = DA9063_M_GPI2, + }, + [DA9063_IRQ_GPI3] = { + .reg_offset = DA9063_REG_EVENT_C_OFFSET, + .mask = DA9063_M_GPI3, + }, + [DA9063_IRQ_GPI4] = { + .reg_offset = DA9063_REG_EVENT_C_OFFSET, + .mask = DA9063_M_GPI4, + }, + [DA9063_IRQ_GPI5] = { + .reg_offset = DA9063_REG_EVENT_C_OFFSET, + .mask = DA9063_M_GPI5, + }, + [DA9063_IRQ_GPI6] = { + .reg_offset = DA9063_REG_EVENT_C_OFFSET, + .mask = DA9063_M_GPI6, + }, + [DA9063_IRQ_GPI7] = { + .reg_offset = DA9063_REG_EVENT_C_OFFSET, + .mask = DA9063_M_GPI7, + }, + /* DA9063 event D register */ + [DA9063_IRQ_GPI8] = { + .reg_offset = DA9063_REG_EVENT_D_OFFSET, + .mask = DA9063_M_GPI8, + }, + [DA9063_IRQ_GPI9] = { + .reg_offset = DA9063_REG_EVENT_D_OFFSET, + .mask = DA9063_M_GPI9, + }, + [DA9063_IRQ_GPI10] = { + .reg_offset = DA9063_REG_EVENT_D_OFFSET, + .mask = DA9063_M_GPI10, + }, + [DA9063_IRQ_GPI11] = { + .reg_offset = DA9063_REG_EVENT_D_OFFSET, + .mask = DA9063_M_GPI11, + }, + [DA9063_IRQ_GPI12] = { + .reg_offset = DA9063_REG_EVENT_D_OFFSET, + .mask = DA9063_M_GPI12, + }, + [DA9063_IRQ_GPI13] = { + .reg_offset = DA9063_REG_EVENT_D_OFFSET, + .mask = DA9063_M_GPI13, + }, + [DA9063_IRQ_GPI14] = { + .reg_offset = DA9063_REG_EVENT_D_OFFSET, + .mask = DA9063_M_GPI14, + }, + [DA9063_IRQ_GPI15] = { + .reg_offset = DA9063_REG_EVENT_D_OFFSET, + .mask = DA9063_M_GPI15, + }, +}; + +static struct regmap_irq_chip da9063_irq_chip = { + .name = "da9063-irq", + .irqs = da9063_irqs, + .num_irqs = DA9063_NUM_IRQ, + + .num_regs = 4, + .status_base = DA9063_REG_EVENT_A, + .mask_base = DA9063_REG_IRQ_MASK_A, + .ack_base = DA9063_REG_EVENT_A, + .init_ack_masked = true, +}; + +int da9063_irq_init(struct da9063 *da9063) +{ + int ret; + + if (!da9063->chip_irq) { + dev_err(da9063->dev, "No IRQ configured\n"); + return -EINVAL; + } + + ret = regmap_add_irq_chip(da9063->regmap, da9063->chip_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED, + da9063->irq_base, &da9063_irq_chip, + &da9063->regmap_irq); + if (ret) { + dev_err(da9063->dev, "Failed to reguest IRQ %d: %d\n", + da9063->chip_irq, ret); + return ret; + } + + return 0; +} + +void da9063_irq_exit(struct da9063 *da9063) +{ + regmap_del_irq_chip(da9063->chip_irq, da9063->regmap_irq); +} diff --git a/include/linux/mfd/da9063/core.h b/include/linux/mfd/da9063/core.h index ec2fd2aa24ae..2d2a0af675fd 100644 --- a/include/linux/mfd/da9063/core.h +++ b/include/linux/mfd/da9063/core.h @@ -33,6 +33,41 @@ enum da9063_models { PMIC_DA9063 = 0x61, }; +/* Interrupts */ +enum da9063_irqs { + DA9063_IRQ_ONKEY = 0, + DA9063_IRQ_ALARM, + DA9063_IRQ_TICK, + DA9063_IRQ_ADC_RDY, + DA9063_IRQ_SEQ_RDY, + DA9063_IRQ_WAKE, + DA9063_IRQ_TEMP, + DA9063_IRQ_COMP_1V2, + DA9063_IRQ_LDO_LIM, + DA9063_IRQ_REG_UVOV, + DA9063_IRQ_VDD_MON, + DA9063_IRQ_WARN, + DA9063_IRQ_GPI0, + DA9063_IRQ_GPI1, + DA9063_IRQ_GPI2, + DA9063_IRQ_GPI3, + DA9063_IRQ_GPI4, + DA9063_IRQ_GPI5, + DA9063_IRQ_GPI6, + DA9063_IRQ_GPI7, + DA9063_IRQ_GPI8, + DA9063_IRQ_GPI9, + DA9063_IRQ_GPI10, + DA9063_IRQ_GPI11, + DA9063_IRQ_GPI12, + DA9063_IRQ_GPI13, + DA9063_IRQ_GPI14, + DA9063_IRQ_GPI15, +}; + +#define DA9063_IRQ_BASE_OFFSET 0 +#define DA9063_NUM_IRQ (DA9063_IRQ_GPI15 + 1 - DA9063_IRQ_BASE_OFFSET) + struct da9063 { /* Device */ struct device *dev; @@ -46,10 +81,13 @@ struct da9063 { /* Interrupts */ int chip_irq; unsigned int irq_base; + struct regmap_irq_chip_data *regmap_irq; }; int da9063_device_init(struct da9063 *da9063, unsigned int irq); +int da9063_irq_init(struct da9063 *da9063); void da9063_device_exit(struct da9063 *da9063); +void da9063_irq_exit(struct da9063 *da9063); #endif /* __MFD_DA9063_CORE_H__ */ -- cgit v1.2.3-71-gd317 From cc01b4639c94b1732995a9909a8973bfed67db2b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 13 Aug 2013 13:23:11 +0530 Subject: mfd: palmas: Add support for external control configuration Some of Palmas resources like clock, SMPSs, LDOs etc can be controlled by external pins ENABLE1, ENABLE2 or NSLEEP. Add support to configure these resources to externally controlled. Signed-off-by: Laxman Dewangan Acked-by: Lee Jones Signed-off-by: Samuel Ortiz --- drivers/mfd/palmas.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/palmas.h | 49 +++++++++++++++++++++++ 2 files changed, 146 insertions(+) (limited to 'include') diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index e4d1c706df8b..e71fa289eb01 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -25,6 +25,52 @@ #include #include +#define PALMAS_EXT_REQ (PALMAS_EXT_CONTROL_ENABLE1 | \ + PALMAS_EXT_CONTROL_ENABLE2 | \ + PALMAS_EXT_CONTROL_NSLEEP) + +struct palmas_sleep_requestor_info { + int id; + int reg_offset; + int bit_pos; +}; + +#define EXTERNAL_REQUESTOR(_id, _offset, _pos) \ + [PALMAS_EXTERNAL_REQSTR_ID_##_id] = { \ + .id = PALMAS_EXTERNAL_REQSTR_ID_##_id, \ + .reg_offset = _offset, \ + .bit_pos = _pos, \ + } + +static struct palmas_sleep_requestor_info sleep_req_info[] = { + EXTERNAL_REQUESTOR(REGEN1, 0, 0), + EXTERNAL_REQUESTOR(REGEN2, 0, 1), + EXTERNAL_REQUESTOR(SYSEN1, 0, 2), + EXTERNAL_REQUESTOR(SYSEN2, 0, 3), + EXTERNAL_REQUESTOR(CLK32KG, 0, 4), + EXTERNAL_REQUESTOR(CLK32KGAUDIO, 0, 5), + EXTERNAL_REQUESTOR(REGEN3, 0, 6), + EXTERNAL_REQUESTOR(SMPS12, 1, 0), + EXTERNAL_REQUESTOR(SMPS3, 1, 1), + EXTERNAL_REQUESTOR(SMPS45, 1, 2), + EXTERNAL_REQUESTOR(SMPS6, 1, 3), + EXTERNAL_REQUESTOR(SMPS7, 1, 4), + EXTERNAL_REQUESTOR(SMPS8, 1, 5), + EXTERNAL_REQUESTOR(SMPS9, 1, 6), + EXTERNAL_REQUESTOR(SMPS10, 1, 7), + EXTERNAL_REQUESTOR(LDO1, 2, 0), + EXTERNAL_REQUESTOR(LDO2, 2, 1), + EXTERNAL_REQUESTOR(LDO3, 2, 2), + EXTERNAL_REQUESTOR(LDO4, 2, 3), + EXTERNAL_REQUESTOR(LDO5, 2, 4), + EXTERNAL_REQUESTOR(LDO6, 2, 5), + EXTERNAL_REQUESTOR(LDO7, 2, 6), + EXTERNAL_REQUESTOR(LDO8, 2, 7), + EXTERNAL_REQUESTOR(LDO9, 3, 0), + EXTERNAL_REQUESTOR(LDOLN, 3, 1), + EXTERNAL_REQUESTOR(LDOUSB, 3, 2), +}; + static const struct regmap_config palmas_regmap_config[PALMAS_NUM_CLIENTS] = { { .reg_bits = 8, @@ -186,6 +232,57 @@ static struct regmap_irq_chip palmas_irq_chip = { PALMAS_INT1_MASK), }; +int palmas_ext_control_req_config(struct palmas *palmas, + enum palmas_external_requestor_id id, int ext_ctrl, bool enable) +{ + int preq_mask_bit = 0; + int reg_add = 0; + int bit_pos; + int ret; + + if (!(ext_ctrl & PALMAS_EXT_REQ)) + return 0; + + if (id >= PALMAS_EXTERNAL_REQSTR_ID_MAX) + return 0; + + if (ext_ctrl & PALMAS_EXT_CONTROL_NSLEEP) { + reg_add = PALMAS_NSLEEP_RES_ASSIGN; + preq_mask_bit = 0; + } else if (ext_ctrl & PALMAS_EXT_CONTROL_ENABLE1) { + reg_add = PALMAS_ENABLE1_RES_ASSIGN; + preq_mask_bit = 1; + } else if (ext_ctrl & PALMAS_EXT_CONTROL_ENABLE2) { + reg_add = PALMAS_ENABLE2_RES_ASSIGN; + preq_mask_bit = 2; + } + + bit_pos = sleep_req_info[id].bit_pos; + reg_add += sleep_req_info[id].reg_offset; + if (enable) + ret = palmas_update_bits(palmas, PALMAS_RESOURCE_BASE, + reg_add, BIT(bit_pos), BIT(bit_pos)); + else + ret = palmas_update_bits(palmas, PALMAS_RESOURCE_BASE, + reg_add, BIT(bit_pos), 0); + if (ret < 0) { + dev_err(palmas->dev, "Resource reg 0x%02x update failed %d\n", + reg_add, ret); + return ret; + } + + /* Unmask the PREQ */ + ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, + PALMAS_POWER_CTRL, BIT(preq_mask_bit), 0); + if (ret < 0) { + dev_err(palmas->dev, "POWER_CTRL register update failed %d\n", + ret); + return ret; + } + return ret; +} +EXPORT_SYMBOL_GPL(palmas_ext_control_req_config); + static int palmas_set_pdata_irq_flag(struct i2c_client *i2c, struct palmas_platform_data *pdata) { diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 1a8dd7afe084..d2fca1c6f7df 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -183,6 +183,50 @@ enum palmas_regulators { PALMAS_NUM_REGS, }; +/* External controll signal name */ +enum { + PALMAS_EXT_CONTROL_ENABLE1 = 0x1, + PALMAS_EXT_CONTROL_ENABLE2 = 0x2, + PALMAS_EXT_CONTROL_NSLEEP = 0x4, +}; + +/* + * Palmas device resources can be controlled externally for + * enabling/disabling it rather than register write through i2c. + * Add the external controlled requestor ID for different resources. + */ +enum palmas_external_requestor_id { + PALMAS_EXTERNAL_REQSTR_ID_REGEN1, + PALMAS_EXTERNAL_REQSTR_ID_REGEN2, + PALMAS_EXTERNAL_REQSTR_ID_SYSEN1, + PALMAS_EXTERNAL_REQSTR_ID_SYSEN2, + PALMAS_EXTERNAL_REQSTR_ID_CLK32KG, + PALMAS_EXTERNAL_REQSTR_ID_CLK32KGAUDIO, + PALMAS_EXTERNAL_REQSTR_ID_REGEN3, + PALMAS_EXTERNAL_REQSTR_ID_SMPS12, + PALMAS_EXTERNAL_REQSTR_ID_SMPS3, + PALMAS_EXTERNAL_REQSTR_ID_SMPS45, + PALMAS_EXTERNAL_REQSTR_ID_SMPS6, + PALMAS_EXTERNAL_REQSTR_ID_SMPS7, + PALMAS_EXTERNAL_REQSTR_ID_SMPS8, + PALMAS_EXTERNAL_REQSTR_ID_SMPS9, + PALMAS_EXTERNAL_REQSTR_ID_SMPS10, + PALMAS_EXTERNAL_REQSTR_ID_LDO1, + PALMAS_EXTERNAL_REQSTR_ID_LDO2, + PALMAS_EXTERNAL_REQSTR_ID_LDO3, + PALMAS_EXTERNAL_REQSTR_ID_LDO4, + PALMAS_EXTERNAL_REQSTR_ID_LDO5, + PALMAS_EXTERNAL_REQSTR_ID_LDO6, + PALMAS_EXTERNAL_REQSTR_ID_LDO7, + PALMAS_EXTERNAL_REQSTR_ID_LDO8, + PALMAS_EXTERNAL_REQSTR_ID_LDO9, + PALMAS_EXTERNAL_REQSTR_ID_LDOLN, + PALMAS_EXTERNAL_REQSTR_ID_LDOUSB, + + /* Last entry */ + PALMAS_EXTERNAL_REQSTR_ID_MAX, +}; + struct palmas_pmic_platform_data { /* An array of pointers to regulator init data indexed by regulator * ID @@ -2866,4 +2910,9 @@ static inline int palmas_irq_get_virq(struct palmas *palmas, int irq) return regmap_irq_get_virq(palmas->irq_data, irq); } + +int palmas_ext_control_req_config(struct palmas *palmas, + enum palmas_external_requestor_id ext_control_req_id, + int ext_ctrl, bool enable); + #endif /* __LINUX_MFD_PALMAS_H */ -- cgit v1.2.3-71-gd317 From bd0eae4e1cd5c4ff7c2a9ebcb3e78ea4631251ef Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 24 Jul 2013 15:05:19 -0700 Subject: PPS: convert class code to use dev_groups The dev_attrs field of struct class is going away soon, dev_groups should be used instead. This converts the pps class code to use the correct field. Cc: Rodolfo Giometti Signed-off-by: Greg Kroah-Hartman --- drivers/pps/pps.c | 2 +- drivers/pps/sysfs.c | 55 +++++++++++++++++++++++++++++----------------- include/linux/pps_kernel.h | 2 +- 3 files changed, 37 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index 7173e3ad475d..2f07cd615665 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c @@ -406,7 +406,7 @@ static int __init pps_init(void) pr_err("failed to allocate class\n"); return PTR_ERR(pps_class); } - pps_class->dev_attrs = pps_attrs; + pps_class->dev_groups = pps_groups; err = alloc_chrdev_region(&pps_devt, 0, PPS_MAX_SOURCES, "pps"); if (err < 0) { diff --git a/drivers/pps/sysfs.c b/drivers/pps/sysfs.c index ef0978c71eee..aefb75d67094 100644 --- a/drivers/pps/sysfs.c +++ b/drivers/pps/sysfs.c @@ -29,8 +29,8 @@ * Attribute functions */ -static ssize_t pps_show_assert(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t assert_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct pps_device *pps = dev_get_drvdata(dev); @@ -41,9 +41,10 @@ static ssize_t pps_show_assert(struct device *dev, (long long) pps->assert_tu.sec, pps->assert_tu.nsec, pps->assert_sequence); } +static DEVICE_ATTR_RO(assert); -static ssize_t pps_show_clear(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t clear_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct pps_device *pps = dev_get_drvdata(dev); @@ -54,45 +55,59 @@ static ssize_t pps_show_clear(struct device *dev, (long long) pps->clear_tu.sec, pps->clear_tu.nsec, pps->clear_sequence); } +static DEVICE_ATTR_RO(clear); -static ssize_t pps_show_mode(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t mode_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct pps_device *pps = dev_get_drvdata(dev); return sprintf(buf, "%4x\n", pps->info.mode); } +static DEVICE_ATTR_RO(mode); -static ssize_t pps_show_echo(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t echo_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct pps_device *pps = dev_get_drvdata(dev); return sprintf(buf, "%d\n", !!pps->info.echo); } +static DEVICE_ATTR_RO(echo); -static ssize_t pps_show_name(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t name_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct pps_device *pps = dev_get_drvdata(dev); return sprintf(buf, "%s\n", pps->info.name); } +static DEVICE_ATTR_RO(name); -static ssize_t pps_show_path(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t path_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct pps_device *pps = dev_get_drvdata(dev); return sprintf(buf, "%s\n", pps->info.path); } +static DEVICE_ATTR_RO(path); + +static struct attribute *pps_attrs[] = { + &dev_attr_assert.attr, + &dev_attr_clear.attr, + &dev_attr_mode.attr, + &dev_attr_echo.attr, + &dev_attr_name.attr, + &dev_attr_path.attr, + NULL, +}; + +static const struct attribute_group pps_group = { + .attrs = pps_attrs, +}; -struct device_attribute pps_attrs[] = { - __ATTR(assert, S_IRUGO, pps_show_assert, NULL), - __ATTR(clear, S_IRUGO, pps_show_clear, NULL), - __ATTR(mode, S_IRUGO, pps_show_mode, NULL), - __ATTR(echo, S_IRUGO, pps_show_echo, NULL), - __ATTR(name, S_IRUGO, pps_show_name, NULL), - __ATTR(path, S_IRUGO, pps_show_path, NULL), - __ATTR_NULL, +const struct attribute_group *pps_groups[] = { + &pps_group, + NULL, }; diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h index 7db3eb93a079..1d2cd21242e8 100644 --- a/include/linux/pps_kernel.h +++ b/include/linux/pps_kernel.h @@ -80,7 +80,7 @@ struct pps_device { * Global variables */ -extern struct device_attribute pps_attrs[]; +extern const struct attribute_group *pps_groups[]; /* * Internal functions. -- cgit v1.2.3-71-gd317 From 7cb14ba75d57910cc4b62115dd5db7bd83c93684 Mon Sep 17 00:00:00 2001 From: Andreas Robinson Date: Tue, 20 Aug 2013 15:33:19 +0930 Subject: modules: add support for soft module dependencies Additional and optional dependencies not found while building the kernel and modules, can now be declared explicitly. Signed-off-by: Andreas Robinson Acked-by: Herbert Xu Signed-off-by: Rusty Russell --- include/linux/module.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/module.h b/include/linux/module.h index 46f1ea01e6f6..504035f3ece1 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -97,6 +97,11 @@ extern const struct gtype##_id __mod_##gtype##_table \ /* For userspace: you can also call me... */ #define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias) +/* Soft module dependencies. See man modprobe.d for details. + * Example: MODULE_SOFTDEP("pre: module-foo module-bar post: module-baz") + */ +#define MODULE_SOFTDEP(_softdep) MODULE_INFO(softdep, _softdep) + /* * The following license idents are currently accepted as indicating free * software modules -- cgit v1.2.3-71-gd317 From ab013c5f60b7ead254863c75b9adc2a47992d01b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 20 Aug 2013 15:33:19 +0930 Subject: module: Add flag to allow mod params to have no arguments Currently the params.c code allows only two "set" functions to have no arguments. If a parameter does not have an argument, then it looks at the set function and tests if it is either param_set_bool() or param_set_bint(). If it is not one of these functions, then it fails the loading of the module. But there may be module parameters that have different set functions and still allow no arguments. But unless each of these cases adds their function to the if statement, it wont be allowed to have no arguments. This method gets rather messing and does not scale. Instead, introduce a flags field to the kernel_param_ops, where if the flag KERNEL_PARAM_FL_NOARG is set, the parameter will not fail if it does not contain an argument. It will be expected that the corresponding set function can handle a NULL pointer as "val". Signed-off-by: Steven Rostedt Signed-off-by: Rusty Russell --- include/linux/moduleparam.h | 13 ++++++++++++- kernel/params.c | 6 ++++-- 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index 27d9da3f86ff..c3eb102a9cc8 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -36,7 +36,18 @@ static const char __UNIQUE_ID(name)[] \ struct kernel_param; +/* + * Flags available for kernel_param_ops + * + * NOARG - the parameter allows for no argument (foo instead of foo=1) + */ +enum { + KERNEL_PARAM_FL_NOARG = (1 << 0) +}; + struct kernel_param_ops { + /* How the ops should behave */ + unsigned int flags; /* Returns 0, or -errno. arg is in kp->arg. */ int (*set)(const char *val, const struct kernel_param *kp); /* Returns length written or -errno. Buffer is 4k (ie. be short!) */ @@ -187,7 +198,7 @@ struct kparam_array /* Obsolete - use module_param_cb() */ #define module_param_call(name, set, get, arg, perm) \ static struct kernel_param_ops __param_ops_##name = \ - { (void *)set, (void *)get }; \ + { 0, (void *)set, (void *)get }; \ __module_param_call(MODULE_PARAM_PREFIX, \ name, &__param_ops_##name, arg, \ (perm) + sizeof(__check_old_set_param(set))*0, -1) diff --git a/kernel/params.c b/kernel/params.c index 59f7ac7bec04..ec4299cfade8 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -103,8 +103,8 @@ static int parse_one(char *param, || params[i].level > max_level) return 0; /* No one handled NULL, so do it here. */ - if (!val && params[i].ops->set != param_set_bool - && params[i].ops->set != param_set_bint) + if (!val && + !(params[i].ops->flags & KERNEL_PARAM_FL_NOARG)) return -EINVAL; pr_debug("handling %s with %p\n", param, params[i].ops->set); @@ -320,6 +320,7 @@ int param_get_bool(char *buffer, const struct kernel_param *kp) EXPORT_SYMBOL(param_get_bool); struct kernel_param_ops param_ops_bool = { + .flags = KERNEL_PARAM_FL_NOARG, .set = param_set_bool, .get = param_get_bool, }; @@ -370,6 +371,7 @@ int param_set_bint(const char *val, const struct kernel_param *kp) EXPORT_SYMBOL(param_set_bint); struct kernel_param_ops param_ops_bint = { + .flags = KERNEL_PARAM_FL_NOARG, .set = param_set_bint, .get = param_get_int, }; -- cgit v1.2.3-71-gd317 From 012a5729ff933ecd07e7470a65d84577aef9ae03 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Mon, 19 Aug 2013 11:23:07 -0700 Subject: vxlan: Extend vxlan handlers for openvswitch. Following patch adds data field to vxlan socket and export vxlan handler api. vh->data is required to store private data per vxlan handler. Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 45 +++++++++++++++++++-------------------------- include/net/vxlan.h | 31 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 26 deletions(-) create mode 100644 include/net/vxlan.h (limited to 'include') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 236c445b1930..aab927b92fb3 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -41,6 +41,7 @@ #include #include #include +#include #define VXLAN_VERSION "0.1" @@ -83,20 +84,6 @@ static int vxlan_net_id; static const u8 all_zeros_mac[ETH_ALEN]; -struct vxlan_sock; -typedef void (vxlan_rcv_t)(struct vxlan_sock *vh, struct sk_buff *skb, __be32 key); - -/* per UDP socket information */ -struct vxlan_sock { - vxlan_rcv_t *rcv; - struct hlist_node hlist; - struct rcu_head rcu; - struct work_struct del_work; - atomic_t refcnt; - struct socket *sock; - struct hlist_head vni_list[VNI_HASH_SIZE]; -}; - /* per-network namespace private data for this module */ struct vxlan_net { struct list_head vxlan_list; @@ -813,8 +800,10 @@ static void vxlan_sock_hold(struct vxlan_sock *vs) atomic_inc(&vs->refcnt); } -static void vxlan_sock_release(struct vxlan_net *vn, struct vxlan_sock *vs) +void vxlan_sock_release(struct vxlan_sock *vs) { + struct vxlan_net *vn = net_generic(sock_net(vs->sock->sk), vxlan_net_id); + if (!atomic_dec_and_test(&vs->refcnt)) return; @@ -824,6 +813,7 @@ static void vxlan_sock_release(struct vxlan_net *vn, struct vxlan_sock *vs) queue_work(vxlan_wq, &vs->del_work); } +EXPORT_SYMBOL_GPL(vxlan_sock_release); /* Callback to update multicast group membership when first VNI on * multicast asddress is brought up @@ -832,7 +822,6 @@ static void vxlan_sock_release(struct vxlan_net *vn, struct vxlan_sock *vs) static void vxlan_igmp_join(struct work_struct *work) { struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev, igmp_join); - struct vxlan_net *vn = net_generic(dev_net(vxlan->dev), vxlan_net_id); struct vxlan_sock *vs = vxlan->vn_sock; struct sock *sk = vs->sock->sk; struct ip_mreqn mreq = { @@ -844,7 +833,7 @@ static void vxlan_igmp_join(struct work_struct *work) ip_mc_join_group(sk, &mreq); release_sock(sk); - vxlan_sock_release(vn, vs); + vxlan_sock_release(vs); dev_put(vxlan->dev); } @@ -852,7 +841,6 @@ static void vxlan_igmp_join(struct work_struct *work) static void vxlan_igmp_leave(struct work_struct *work) { struct vxlan_dev *vxlan = container_of(work, struct vxlan_dev, igmp_leave); - struct vxlan_net *vn = net_generic(dev_net(vxlan->dev), vxlan_net_id); struct vxlan_sock *vs = vxlan->vn_sock; struct sock *sk = vs->sock->sk; struct ip_mreqn mreq = { @@ -864,7 +852,7 @@ static void vxlan_igmp_leave(struct work_struct *work) ip_mc_leave_group(sk, &mreq); release_sock(sk); - vxlan_sock_release(vn, vs); + vxlan_sock_release(vs); dev_put(vxlan->dev); } @@ -1429,13 +1417,12 @@ static void vxlan_fdb_delete_default(struct vxlan_dev *vxlan) static void vxlan_uninit(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); - struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); struct vxlan_sock *vs = vxlan->vn_sock; vxlan_fdb_delete_default(vxlan); if (vs) - vxlan_sock_release(vn, vs); + vxlan_sock_release(vs); free_percpu(dev->tstats); } @@ -1653,7 +1640,7 @@ static void vxlan_del_work(struct work_struct *work) } static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, - vxlan_rcv_t *rcv) + vxlan_rcv_t *rcv, void *data) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); struct vxlan_sock *vs; @@ -1700,6 +1687,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, } atomic_set(&vs->refcnt, 1); vs->rcv = rcv; + vs->data = data; /* Disable multicast loopback */ inet_sk(sk)->mc_loop = 0; @@ -1714,16 +1702,20 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, return vs; } -static struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, - vxlan_rcv_t *rcv) +struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, + vxlan_rcv_t *rcv, void *data, + bool no_share) { struct vxlan_net *vn = net_generic(net, vxlan_net_id); struct vxlan_sock *vs; - vs = vxlan_socket_create(net, port, rcv); + vs = vxlan_socket_create(net, port, rcv, data); if (!IS_ERR(vs)) return vs; + if (no_share) /* Return error if sharing is not allowed. */ + return vs; + spin_lock(&vn->sock_lock); vs = vxlan_find_sock(net, port); if (vs) { @@ -1739,6 +1731,7 @@ static struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, return vs; } +EXPORT_SYMBOL_GPL(vxlan_sock_add); /* Scheduled at device creation to bind to a socket */ static void vxlan_sock_work(struct work_struct *work) @@ -1749,7 +1742,7 @@ static void vxlan_sock_work(struct work_struct *work) __be16 port = vxlan->dst_port; struct vxlan_sock *nvs; - nvs = vxlan_sock_add(net, port, vxlan_rcv); + nvs = vxlan_sock_add(net, port, vxlan_rcv, NULL, false); spin_lock(&vn->sock_lock); if (!IS_ERR(nvs)) vxlan_vs_add_dev(nvs, vxlan); diff --git a/include/net/vxlan.h b/include/net/vxlan.h new file mode 100644 index 000000000000..43de27585cb0 --- /dev/null +++ b/include/net/vxlan.h @@ -0,0 +1,31 @@ +#ifndef __NET_VXLAN_H +#define __NET_VXLAN_H 1 + +#include +#include +#include + +#define VNI_HASH_BITS 10 +#define VNI_HASH_SIZE (1< Date: Mon, 19 Aug 2013 11:23:17 -0700 Subject: vxlan: Factor out vxlan send api. Following patch allows more code sharing between vxlan and ovs-vxlan. Signed-off-by: Pravin B Shelar Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 91 +++++++++++++++++++++++++++++++---------------------- include/net/vxlan.h | 8 +++++ 2 files changed, 62 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index aab927b92fb3..f3496e995302 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -1082,11 +1082,8 @@ static void vxlan_sock_put(struct sk_buff *skb) } /* On transmit, associate with the tunnel socket */ -static void vxlan_set_owner(struct net_device *dev, struct sk_buff *skb) +static void vxlan_set_owner(struct sock *sk, struct sk_buff *skb) { - struct vxlan_dev *vxlan = netdev_priv(dev); - struct sock *sk = vxlan->vn_sock->sock->sk; - skb_orphan(skb); sock_hold(sk); skb->sk = sk; @@ -1098,9 +1095,9 @@ static void vxlan_set_owner(struct net_device *dev, struct sk_buff *skb) * better and maybe available from hardware * secondary choice is to use jhash on the Ethernet header */ -static __be16 vxlan_src_port(const struct vxlan_dev *vxlan, struct sk_buff *skb) +__be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb) { - unsigned int range = (vxlan->port_max - vxlan->port_min) + 1; + unsigned int range = (port_max - port_min) + 1; u32 hash; hash = skb_get_rxhash(skb); @@ -1108,8 +1105,9 @@ static __be16 vxlan_src_port(const struct vxlan_dev *vxlan, struct sk_buff *skb) hash = jhash(skb->data, 2 * ETH_ALEN, (__force u32) skb->protocol); - return htons((((u64) hash * range) >> 32) + vxlan->port_min); + return htons((((u64) hash * range) >> 32) + port_min); } +EXPORT_SYMBOL_GPL(vxlan_src_port); static int handle_offloads(struct sk_buff *skb) { @@ -1125,6 +1123,45 @@ static int handle_offloads(struct sk_buff *skb) return 0; } +int vxlan_xmit_skb(struct net *net, struct vxlan_sock *vs, + struct rtable *rt, struct sk_buff *skb, + __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, + __be16 src_port, __be16 dst_port, __be32 vni) +{ + struct vxlanhdr *vxh; + struct udphdr *uh; + int err; + + if (!skb->encapsulation) { + skb_reset_inner_headers(skb); + skb->encapsulation = 1; + } + + vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh)); + vxh->vx_flags = htonl(VXLAN_FLAGS); + vxh->vx_vni = vni; + + __skb_push(skb, sizeof(*uh)); + skb_reset_transport_header(skb); + uh = udp_hdr(skb); + + uh->dest = dst_port; + uh->source = src_port; + + uh->len = htons(skb->len); + uh->check = 0; + + vxlan_set_owner(vs->sock->sk, skb); + + err = handle_offloads(skb); + if (err) + return err; + + return iptunnel_xmit(net, rt, skb, src, dst, + IPPROTO_UDP, tos, ttl, df); +} +EXPORT_SYMBOL_GPL(vxlan_xmit_skb); + /* Bypass encapsulation if the destination is local */ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, struct vxlan_dev *dst_vxlan) @@ -1162,8 +1199,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, struct vxlan_dev *vxlan = netdev_priv(dev); struct rtable *rt; const struct iphdr *old_iph; - struct vxlanhdr *vxh; - struct udphdr *uh; struct flowi4 fl4; __be32 dst; __be16 src_port, dst_port; @@ -1185,11 +1220,6 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, goto drop; } - if (!skb->encapsulation) { - skb_reset_inner_headers(skb); - skb->encapsulation = 1; - } - /* Need space for new headers (invalidates iph ptr) */ if (skb_cow_head(skb, VXLAN_HEADROOM)) goto drop; @@ -1204,7 +1234,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, if (tos == 1) tos = ip_tunnel_get_dsfield(old_iph, skb); - src_port = vxlan_src_port(vxlan, skb); + src_port = vxlan_src_port(vxlan->port_min, vxlan->port_max, skb); memset(&fl4, 0, sizeof(fl4)); fl4.flowi4_oif = rdst->remote_ifindex; @@ -1221,9 +1251,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, if (rt->dst.dev == dev) { netdev_dbg(dev, "circular route to %pI4\n", &dst); - ip_rt_put(rt); dev->stats.collisions++; - goto tx_error; + goto rt_tx_error; } /* Bypass encapsulation if the destination is local */ @@ -1238,30 +1267,16 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, vxlan_encap_bypass(skb, vxlan, dst_vxlan); return; } - vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh)); - vxh->vx_flags = htonl(VXLAN_FLAGS); - vxh->vx_vni = htonl(vni << 8); - - __skb_push(skb, sizeof(*uh)); - skb_reset_transport_header(skb); - uh = udp_hdr(skb); - - uh->dest = dst_port; - uh->source = src_port; - - uh->len = htons(skb->len); - uh->check = 0; - - vxlan_set_owner(dev, skb); - - if (handle_offloads(skb)) - goto drop; tos = ip_tunnel_ecn_encap(tos, old_iph, skb); ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); - err = iptunnel_xmit(dev_net(dev), rt, skb, fl4.saddr, dst, - IPPROTO_UDP, tos, ttl, df); + err = vxlan_xmit_skb(dev_net(dev), vxlan->vn_sock, rt, skb, + fl4.saddr, dst, tos, ttl, df, + src_port, dst_port, htonl(vni << 8)); + + if (err < 0) + goto rt_tx_error; iptunnel_xmit_stats(err, &dev->stats, dev->tstats); return; @@ -1270,6 +1285,8 @@ drop: dev->stats.tx_dropped++; goto tx_free; +rt_tx_error: + ip_rt_put(rt); tx_error: dev->stats.tx_errors++; tx_free: diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 43de27585cb0..ad342e3688a0 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -28,4 +28,12 @@ struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, bool no_share); void vxlan_sock_release(struct vxlan_sock *vs); + +int vxlan_xmit_skb(struct net *net, struct vxlan_sock *vs, + struct rtable *rt, struct sk_buff *skb, + __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, + __be16 src_port, __be16 dst_port, __be32 vni); + +__be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb); + #endif -- cgit v1.2.3-71-gd317 From 58264848a5a7b91195f43c4729072e8cc980288d Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Mon, 19 Aug 2013 11:23:34 -0700 Subject: openvswitch: Add vxlan tunneling support. Following patch adds vxlan vport type for openvswitch using vxlan api. So now there is vxlan dependency for openvswitch. CC: Jesse Gross Signed-off-by: Pravin B Shelar Acked-by: Jesse Gross Signed-off-by: David S. Miller --- include/uapi/linux/openvswitch.h | 11 +++ net/openvswitch/Kconfig | 13 +++ net/openvswitch/Makefile | 4 + net/openvswitch/vport-vxlan.c | 204 +++++++++++++++++++++++++++++++++++++++ net/openvswitch/vport.c | 3 + net/openvswitch/vport.h | 1 + 6 files changed, 236 insertions(+) create mode 100644 net/openvswitch/vport-vxlan.c (limited to 'include') diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index c55efaaa9bb4..52490b0e62b5 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -165,6 +165,7 @@ enum ovs_vport_type { OVS_VPORT_TYPE_NETDEV, /* network device */ OVS_VPORT_TYPE_INTERNAL, /* network device implemented by datapath */ OVS_VPORT_TYPE_GRE, /* GRE tunnel. */ + OVS_VPORT_TYPE_VXLAN, /* VXLAN tunnel. */ __OVS_VPORT_TYPE_MAX }; @@ -211,6 +212,16 @@ enum ovs_vport_attr { #define OVS_VPORT_ATTR_MAX (__OVS_VPORT_ATTR_MAX - 1) +/* OVS_VPORT_ATTR_OPTIONS attributes for tunnels. + */ +enum { + OVS_TUNNEL_ATTR_UNSPEC, + OVS_TUNNEL_ATTR_DST_PORT, /* 16-bit UDP port, used by L4 tunnels. */ + __OVS_TUNNEL_ATTR_MAX +}; + +#define OVS_TUNNEL_ATTR_MAX (__OVS_TUNNEL_ATTR_MAX - 1) + /* Flows. */ #define OVS_FLOW_FAMILY "ovs_flow" diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig index 27ee56b688a3..bed30e69baa7 100644 --- a/net/openvswitch/Kconfig +++ b/net/openvswitch/Kconfig @@ -40,3 +40,16 @@ config OPENVSWITCH_GRE Say N to exclude this support and reduce the binary size. If unsure, say Y. + +config OPENVSWITCH_VXLAN + bool "Open vSwitch VXLAN tunneling support" + depends on INET + depends on OPENVSWITCH + depends on VXLAN && !(OPENVSWITCH=y && VXLAN=m) + default y + ---help--- + If you say Y here, then the Open vSwitch will be able create vxlan vport. + + Say N to exclude this support and reduce the binary size. + + If unsure, say Y. diff --git a/net/openvswitch/Makefile b/net/openvswitch/Makefile index 01bddb2991e3..82e4ee54a44b 100644 --- a/net/openvswitch/Makefile +++ b/net/openvswitch/Makefile @@ -13,3 +13,7 @@ openvswitch-y := \ vport-gre.o \ vport-internal_dev.o \ vport-netdev.o + +ifneq ($(CONFIG_OPENVSWITCH_VXLAN),) +openvswitch-y += vport-vxlan.o +endif diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c new file mode 100644 index 000000000000..36848bd54a77 --- /dev/null +++ b/net/openvswitch/vport-vxlan.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2013 Nicira, Inc. + * Copyright (c) 2013 Cisco Systems, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "datapath.h" +#include "vport.h" + +/** + * struct vxlan_port - Keeps track of open UDP ports + * @vs: vxlan_sock created for the port. + * @name: vport name. + */ +struct vxlan_port { + struct vxlan_sock *vs; + char name[IFNAMSIZ]; +}; + +static inline struct vxlan_port *vxlan_vport(const struct vport *vport) +{ + return vport_priv(vport); +} + +/* Called with rcu_read_lock and BH disabled. */ +static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb, __be32 vx_vni) +{ + struct ovs_key_ipv4_tunnel tun_key; + struct vport *vport = vs->data; + struct iphdr *iph; + __be64 key; + + /* Save outer tunnel values */ + iph = ip_hdr(skb); + key = cpu_to_be64(ntohl(vx_vni) >> 8); + ovs_flow_tun_key_init(&tun_key, iph, key, TUNNEL_KEY); + + ovs_vport_receive(vport, skb, &tun_key); +} + +static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb) +{ + struct vxlan_port *vxlan_port = vxlan_vport(vport); + __be16 dst_port = inet_sk(vxlan_port->vs->sock->sk)->inet_sport; + + if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(dst_port))) + return -EMSGSIZE; + return 0; +} + +static void vxlan_tnl_destroy(struct vport *vport) +{ + struct vxlan_port *vxlan_port = vxlan_vport(vport); + + vxlan_sock_release(vxlan_port->vs); + + ovs_vport_deferred_free(vport); +} + +static struct vport *vxlan_tnl_create(const struct vport_parms *parms) +{ + struct net *net = ovs_dp_get_net(parms->dp); + struct nlattr *options = parms->options; + struct vxlan_port *vxlan_port; + struct vxlan_sock *vs; + struct vport *vport; + struct nlattr *a; + u16 dst_port; + int err; + + if (!options) { + err = -EINVAL; + goto error; + } + a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT); + if (a && nla_len(a) == sizeof(u16)) { + dst_port = nla_get_u16(a); + } else { + /* Require destination port from userspace. */ + err = -EINVAL; + goto error; + } + + vport = ovs_vport_alloc(sizeof(struct vxlan_port), + &ovs_vxlan_vport_ops, parms); + if (IS_ERR(vport)) + return vport; + + vxlan_port = vxlan_vport(vport); + strncpy(vxlan_port->name, parms->name, IFNAMSIZ); + + vs = vxlan_sock_add(net, htons(dst_port), vxlan_rcv, vport, true); + if (IS_ERR(vs)) { + ovs_vport_free(vport); + return (void *)vs; + } + vxlan_port->vs = vs; + + return vport; + +error: + return ERR_PTR(err); +} + +static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb) +{ + struct net *net = ovs_dp_get_net(vport->dp); + struct vxlan_port *vxlan_port = vxlan_vport(vport); + __be16 dst_port = inet_sk(vxlan_port->vs->sock->sk)->inet_sport; + struct rtable *rt; + struct flowi4 fl; + __be16 src_port; + int port_min; + int port_max; + __be16 df; + int err; + + if (unlikely(!OVS_CB(skb)->tun_key)) { + err = -EINVAL; + goto error; + } + + /* Route lookup */ + memset(&fl, 0, sizeof(fl)); + fl.daddr = OVS_CB(skb)->tun_key->ipv4_dst; + fl.saddr = OVS_CB(skb)->tun_key->ipv4_src; + fl.flowi4_tos = RT_TOS(OVS_CB(skb)->tun_key->ipv4_tos); + fl.flowi4_mark = skb->mark; + fl.flowi4_proto = IPPROTO_UDP; + + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + goto error; + } + + df = OVS_CB(skb)->tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? + htons(IP_DF) : 0; + + skb->local_df = 1; + + inet_get_local_port_range(&port_min, &port_max); + src_port = vxlan_src_port(port_min, port_max, skb); + + err = vxlan_xmit_skb(net, vxlan_port->vs, rt, skb, + fl.saddr, OVS_CB(skb)->tun_key->ipv4_dst, + OVS_CB(skb)->tun_key->ipv4_tos, + OVS_CB(skb)->tun_key->ipv4_ttl, df, + src_port, dst_port, + htonl(be64_to_cpu(OVS_CB(skb)->tun_key->tun_id) << 8)); + if (err < 0) + ip_rt_put(rt); +error: + return err; +} + +static const char *vxlan_get_name(const struct vport *vport) +{ + struct vxlan_port *vxlan_port = vxlan_vport(vport); + return vxlan_port->name; +} + +const struct vport_ops ovs_vxlan_vport_ops = { + .type = OVS_VPORT_TYPE_VXLAN, + .create = vxlan_tnl_create, + .destroy = vxlan_tnl_destroy, + .get_name = vxlan_get_name, + .get_options = vxlan_get_options, + .send = vxlan_tnl_send, +}; diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index d4c7fa04ce08..d69e0c06dfde 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -42,6 +42,9 @@ static const struct vport_ops *vport_ops_list[] = { #ifdef CONFIG_OPENVSWITCH_GRE &ovs_gre_vport_ops, #endif +#ifdef CONFIG_OPENVSWITCH_VXLAN + &ovs_vxlan_vport_ops, +#endif }; /* Protected by RCU read lock for reading, ovs_mutex for writing. */ diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h index 376045c42f8b..1a9fbcec6e1b 100644 --- a/net/openvswitch/vport.h +++ b/net/openvswitch/vport.h @@ -199,6 +199,7 @@ void ovs_vport_record_error(struct vport *, enum vport_err_type err_type); extern const struct vport_ops ovs_netdev_vport_ops; extern const struct vport_ops ovs_internal_vport_ops; extern const struct vport_ops ovs_gre_vport_ops; +extern const struct vport_ops ovs_vxlan_vport_ops; static inline void ovs_skb_postpush_rcsum(struct sk_buff *skb, const void *start, unsigned int len) -- cgit v1.2.3-71-gd317 From 773ccdfd9cc6f9bf8ec75a59fa742d7a663a5903 Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 20 Aug 2013 14:18:51 +0800 Subject: mfd: rtsx: Read vendor setting from config space Normally OEMs will set vendor setting to the config space of Realtek card reader in BIOS stage. This patch reads the setting at the first, and configure the internal registers according to it, to improve card reader's compatibility condition. Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/rtl8411.c | 77 ++++++++++++++++++++++++++++++++++--- drivers/mfd/rts5209.c | 48 +++++++++++++++-------- drivers/mfd/rts5227.c | 91 ++++++++++++++++++++++++++++++++++---------- drivers/mfd/rts5229.c | 38 ++++++++++++++++-- drivers/mfd/rts5249.c | 90 +++++++++++++++++++++++++++++++++---------- drivers/mfd/rtsx_pcr.c | 26 +++++++++++-- drivers/mfd/rtsx_pcr.h | 29 ++++++++++++++ include/linux/mfd/rtsx_pci.h | 34 ++++++++++++++++- 8 files changed, 365 insertions(+), 68 deletions(-) (limited to 'include') diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c index c436bf27e78d..5a68c9bdeddf 100644 --- a/drivers/mfd/rtl8411.c +++ b/drivers/mfd/rtl8411.c @@ -47,19 +47,70 @@ static int rtl8411b_is_qfn48(struct rtsx_pcr *pcr) return 0; } +static void rtl8411_fetch_vendor_settings(struct rtsx_pcr *pcr) +{ + u32 reg1; + u8 reg3; + + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®1); + dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg1); + + if (!rtsx_vendor_setting_valid(reg1)) + return; + + pcr->aspm_en = rtsx_reg_to_aspm(reg1); + pcr->sd30_drive_sel_1v8 = + map_sd_drive(rtsx_reg_to_sd30_drive_sel_1v8(reg1)); + pcr->card_drive_sel &= 0x3F; + pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg1); + + rtsx_pci_read_config_byte(pcr, PCR_SETTING_REG3, ®3); + dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG3, reg3); + pcr->sd30_drive_sel_3v3 = rtl8411_reg_to_sd30_drive_sel_3v3(reg3); +} + +static void rtl8411b_fetch_vendor_settings(struct rtsx_pcr *pcr) +{ + u32 reg; + + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); + + if (!rtsx_vendor_setting_valid(reg)) + return; + + pcr->aspm_en = rtsx_reg_to_aspm(reg); + pcr->sd30_drive_sel_1v8 = + map_sd_drive(rtsx_reg_to_sd30_drive_sel_1v8(reg)); + pcr->sd30_drive_sel_3v3 = + map_sd_drive(rtl8411b_reg_to_sd30_drive_sel_3v3(reg)); +} + static int rtl8411_extra_init_hw(struct rtsx_pcr *pcr) { - return rtsx_pci_write_register(pcr, CD_PAD_CTL, + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL, + 0xFF, pcr->sd30_drive_sel_3v3); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CD_PAD_CTL, CD_DISABLE_MASK | CD_AUTO_DISABLE, CD_ENABLE); + + return rtsx_pci_send_cmd(pcr, 100); } static int rtl8411b_extra_init_hw(struct rtsx_pcr *pcr) { - if (rtl8411b_is_qfn48(pcr)) - rtsx_pci_write_register(pcr, CARD_PULL_CTL3, 0xFF, 0xF5); + rtsx_pci_init_cmd(pcr); - return rtsx_pci_write_register(pcr, CD_PAD_CTL, + if (rtl8411b_is_qfn48(pcr)) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + CARD_PULL_CTL3, 0xFF, 0xF5); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL, + 0xFF, pcr->sd30_drive_sel_3v3); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CD_PAD_CTL, CD_DISABLE_MASK | CD_AUTO_DISABLE, CD_ENABLE); + + return rtsx_pci_send_cmd(pcr, 100); } static int rtl8411_turn_on_led(struct rtsx_pcr *pcr) @@ -141,13 +192,13 @@ static int rtl8411_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) mask = (BPP_REG_TUNED18 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_MASK; if (voltage == OUTPUT_3V3) { err = rtsx_pci_write_register(pcr, - SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D); + SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_3v3); if (err < 0) return err; val = (BPP_ASIC_3V3 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_3V3; } else if (voltage == OUTPUT_1V8) { err = rtsx_pci_write_register(pcr, - SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B); + SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_1v8); if (err < 0) return err; val = (BPP_ASIC_1V8 << BPP_TUNED18_SHIFT_8411) | BPP_PAD_1V8; @@ -222,6 +273,7 @@ static int rtl8411_conv_clk_and_div_n(int input, int dir) } static const struct pcr_ops rtl8411_pcr_ops = { + .fetch_vendor_settings = rtl8411_fetch_vendor_settings, .extra_init_hw = rtl8411_extra_init_hw, .optimize_phy = NULL, .turn_on_led = rtl8411_turn_on_led, @@ -236,6 +288,7 @@ static const struct pcr_ops rtl8411_pcr_ops = { }; static const struct pcr_ops rtl8411b_pcr_ops = { + .fetch_vendor_settings = rtl8411b_fetch_vendor_settings, .extra_init_hw = rtl8411b_extra_init_hw, .optimize_phy = NULL, .turn_on_led = rtl8411_turn_on_led, @@ -385,6 +438,12 @@ void rtl8411_init_params(struct rtsx_pcr *pcr) pcr->num_slots = 2; pcr->ops = &rtl8411_pcr_ops; + pcr->flags = 0; + pcr->card_drive_sel = RTL8411_CARD_DRIVE_DEFAULT; + pcr->sd30_drive_sel_1v8 = DRIVER_TYPE_B; + pcr->sd30_drive_sel_3v3 = DRIVER_TYPE_D; + pcr->aspm_en = ASPM_L1_EN; + pcr->ic_version = rtl8411_get_ic_version(pcr); pcr->sd_pull_ctl_enable_tbl = rtl8411_sd_pull_ctl_enable_tbl; pcr->sd_pull_ctl_disable_tbl = rtl8411_sd_pull_ctl_disable_tbl; @@ -398,6 +457,12 @@ void rtl8411b_init_params(struct rtsx_pcr *pcr) pcr->num_slots = 2; pcr->ops = &rtl8411b_pcr_ops; + pcr->flags = 0; + pcr->card_drive_sel = RTL8411_CARD_DRIVE_DEFAULT; + pcr->sd30_drive_sel_1v8 = DRIVER_TYPE_B; + pcr->sd30_drive_sel_3v3 = DRIVER_TYPE_D; + pcr->aspm_en = ASPM_L1_EN; + pcr->ic_version = rtl8411_get_ic_version(pcr); if (rtl8411b_is_qfn48(pcr)) { diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c index ec78d9fb0879..2170449bd7e5 100644 --- a/drivers/mfd/rts5209.c +++ b/drivers/mfd/rts5209.c @@ -34,18 +34,28 @@ static u8 rts5209_get_ic_version(struct rtsx_pcr *pcr) return val & 0x0F; } -static void rts5209_init_vendor_cfg(struct rtsx_pcr *pcr) +static void rts5209_fetch_vendor_settings(struct rtsx_pcr *pcr) { - u32 val; + u32 reg; - rtsx_pci_read_config_dword(pcr, 0x724, &val); - dev_dbg(&(pcr->pci->dev), "Cfg 0x724: 0x%x\n", val); + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); - if (!(val & 0x80)) { - if (val & 0x08) - pcr->ms_pmos = false; - else - pcr->ms_pmos = true; + if (rts5209_vendor_setting1_valid(reg)) { + if (rts5209_reg_check_ms_pmos(reg)) + pcr->flags |= PCR_MS_PMOS; + pcr->aspm_en = rts5209_reg_to_aspm(reg); + } + + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); + + if (rts5209_vendor_setting2_valid(reg)) { + pcr->sd30_drive_sel_1v8 = + rts5209_reg_to_sd30_drive_sel_1v8(reg); + pcr->sd30_drive_sel_3v3 = + rts5209_reg_to_sd30_drive_sel_3v3(reg); + pcr->card_drive_sel = rts5209_reg_to_card_drive_sel(reg); } } @@ -57,6 +67,9 @@ static int rts5209_extra_init_hw(struct rtsx_pcr *pcr) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_GPIO, 0xFF, 0x03); /* Configure GPIO as output */ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_GPIO_DIR, 0xFF, 0x03); + /* Configure driving */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL, + 0xFF, pcr->sd30_drive_sel_3v3); return rtsx_pci_send_cmd(pcr, 100); } @@ -95,7 +108,7 @@ static int rts5209_card_power_on(struct rtsx_pcr *pcr, int card) partial_pwr_on = SD_PARTIAL_POWER_ON; pwr_on = SD_POWER_ON; - if (pcr->ms_pmos && (card == RTSX_MS_CARD)) { + if ((pcr->flags & PCR_MS_PMOS) && (card == RTSX_MS_CARD)) { pwr_mask = MS_POWER_MASK; partial_pwr_on = MS_PARTIAL_POWER_ON; pwr_on = MS_POWER_ON; @@ -131,7 +144,7 @@ static int rts5209_card_power_off(struct rtsx_pcr *pcr, int card) pwr_mask = SD_POWER_MASK; pwr_off = SD_POWER_OFF; - if (pcr->ms_pmos && (card == RTSX_MS_CARD)) { + if ((pcr->flags & PCR_MS_PMOS) && (card == RTSX_MS_CARD)) { pwr_mask = MS_POWER_MASK; pwr_off = MS_POWER_OFF; } @@ -140,7 +153,7 @@ static int rts5209_card_power_off(struct rtsx_pcr *pcr, int card) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, pwr_mask | PMOS_STRG_MASK, pwr_off | PMOS_STRG_400mA); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0X06); + LDO3318_PWR_MASK, 0x06); return rtsx_pci_send_cmd(pcr, 100); } @@ -150,7 +163,7 @@ static int rts5209_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) if (voltage == OUTPUT_3V3) { err = rtsx_pci_write_register(pcr, - SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D); + SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_3v3); if (err < 0) return err; err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); @@ -158,7 +171,7 @@ static int rts5209_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) return err; } else if (voltage == OUTPUT_1V8) { err = rtsx_pci_write_register(pcr, - SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B); + SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_1v8); if (err < 0) return err; err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); @@ -172,6 +185,7 @@ static int rts5209_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) } static const struct pcr_ops rts5209_pcr_ops = { + .fetch_vendor_settings = rts5209_fetch_vendor_settings, .extra_init_hw = rts5209_extra_init_hw, .optimize_phy = rts5209_optimize_phy, .turn_on_led = rts5209_turn_on_led, @@ -242,7 +256,11 @@ void rts5209_init_params(struct rtsx_pcr *pcr) pcr->num_slots = 2; pcr->ops = &rts5209_pcr_ops; - rts5209_init_vendor_cfg(pcr); + pcr->flags = 0; + pcr->card_drive_sel = RTS5209_CARD_DRIVE_DEFAULT; + pcr->sd30_drive_sel_1v8 = DRIVER_TYPE_B; + pcr->sd30_drive_sel_3v3 = DRIVER_TYPE_D; + pcr->aspm_en = ASPM_L1_EN; pcr->ic_version = rts5209_get_ic_version(pcr); pcr->sd_pull_ctl_enable_tbl = rts5209_sd_pull_ctl_enable_tbl; diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c index fc831dcb1480..c3181d71fedd 100644 --- a/drivers/mfd/rts5227.c +++ b/drivers/mfd/rts5227.c @@ -29,6 +29,60 @@ #include "rtsx_pcr.h" +static void rts5227_fill_driving(struct rtsx_pcr *pcr, u8 voltage) +{ + u8 driving_3v3[4][3] = { + {0x13, 0x13, 0x13}, + {0x96, 0x96, 0x96}, + {0x7F, 0x7F, 0x7F}, + {0x96, 0x96, 0x96}, + }; + u8 driving_1v8[4][3] = { + {0x99, 0x99, 0x99}, + {0xAA, 0xAA, 0xAA}, + {0xFE, 0xFE, 0xFE}, + {0xB3, 0xB3, 0xB3}, + }; + u8 (*driving)[3], drive_sel; + + if (voltage == OUTPUT_3V3) { + driving = driving_3v3; + drive_sel = pcr->sd30_drive_sel_3v3; + } else { + driving = driving_1v8; + drive_sel = pcr->sd30_drive_sel_1v8; + } + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL, + 0xFF, driving[drive_sel][0]); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL, + 0xFF, driving[drive_sel][1]); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL, + 0xFF, driving[drive_sel][2]); +} + +static void rts5227_fetch_vendor_settings(struct rtsx_pcr *pcr) +{ + u32 reg; + + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); + + if (!rtsx_vendor_setting_valid(reg)) + return; + + pcr->aspm_en = rtsx_reg_to_aspm(reg); + pcr->sd30_drive_sel_1v8 = rtsx_reg_to_sd30_drive_sel_1v8(reg); + pcr->card_drive_sel &= 0x3F; + pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); + + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); + pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); + if (rtsx_reg_check_reverse_socket(reg)) + pcr->flags |= PCR_REVERSE_SOCKET; +} + static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) { u16 cap; @@ -48,17 +102,15 @@ static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LTR_CTL, 0xFF, 0xA3); /* Configure OBFF */ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OBFF_CFG, 0x03, 0x03); - /* Configure force_clock_req - * Maybe We should define 0xFF03 as some name - */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, 0xFF03, 0x08, 0x08); - /* Correct driving */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - SD30_CLK_DRIVE_SEL, 0xFF, 0x96); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - SD30_CMD_DRIVE_SEL, 0xFF, 0x96); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - SD30_DAT_DRIVE_SEL, 0xFF, 0x96); + /* Configure driving */ + rts5227_fill_driving(pcr, OUTPUT_3V3); + /* Configure force_clock_req */ + if (pcr->flags & PCR_REVERSE_SOCKET) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + AUTOLOAD_CFG_BASE + 3, 0xB8, 0xB8); + else + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + AUTOLOAD_CFG_BASE + 3, 0xB8, 0x88); return rtsx_pci_send_cmd(pcr, 100); } @@ -131,13 +183,11 @@ static int rts5227_card_power_off(struct rtsx_pcr *pcr, int card) static int rts5227_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) { int err; - u8 drive_sel; if (voltage == OUTPUT_3V3) { err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); if (err < 0) return err; - drive_sel = 0x96; } else if (voltage == OUTPUT_1V8) { err = rtsx_pci_write_phy_register(pcr, 0x11, 0x3C02); if (err < 0) @@ -145,23 +195,18 @@ static int rts5227_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C80 | 0x24); if (err < 0) return err; - drive_sel = 0xB3; } else { return -EINVAL; } /* set pad drive */ rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL, - 0xFF, drive_sel); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL, - 0xFF, drive_sel); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL, - 0xFF, drive_sel); + rts5227_fill_driving(pcr, voltage); return rtsx_pci_send_cmd(pcr, 100); } static const struct pcr_ops rts5227_pcr_ops = { + .fetch_vendor_settings = rts5227_fetch_vendor_settings, .extra_init_hw = rts5227_extra_init_hw, .optimize_phy = rts5227_optimize_phy, .turn_on_led = rts5227_turn_on_led, @@ -227,6 +272,12 @@ void rts5227_init_params(struct rtsx_pcr *pcr) pcr->num_slots = 2; pcr->ops = &rts5227_pcr_ops; + pcr->flags = 0; + pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT; + pcr->sd30_drive_sel_1v8 = CFG_DRIVER_TYPE_B; + pcr->sd30_drive_sel_3v3 = CFG_DRIVER_TYPE_B; + pcr->aspm_en = ASPM_L1_EN; + pcr->sd_pull_ctl_enable_tbl = rts5227_sd_pull_ctl_enable_tbl; pcr->sd_pull_ctl_disable_tbl = rts5227_sd_pull_ctl_disable_tbl; pcr->ms_pull_ctl_enable_tbl = rts5227_ms_pull_ctl_enable_tbl; diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c index 58af4dbe3586..7a1ad6dd2917 100644 --- a/drivers/mfd/rts5229.c +++ b/drivers/mfd/rts5229.c @@ -34,6 +34,28 @@ static u8 rts5229_get_ic_version(struct rtsx_pcr *pcr) return val & 0x0F; } +static void rts5229_fetch_vendor_settings(struct rtsx_pcr *pcr) +{ + u32 reg; + + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); + + if (!rtsx_vendor_setting_valid(reg)) + return; + + pcr->aspm_en = rtsx_reg_to_aspm(reg); + pcr->sd30_drive_sel_1v8 = + map_sd_drive(rtsx_reg_to_sd30_drive_sel_1v8(reg)); + pcr->card_drive_sel &= 0x3F; + pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); + + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); + pcr->sd30_drive_sel_3v3 = + map_sd_drive(rtsx_reg_to_sd30_drive_sel_3v3(reg)); +} + static int rts5229_extra_init_hw(struct rtsx_pcr *pcr) { rtsx_pci_init_cmd(pcr); @@ -45,6 +67,9 @@ static int rts5229_extra_init_hw(struct rtsx_pcr *pcr) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01); /* LED shine disabled, set initial shine cycle period */ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02); + /* Configure driving */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL, + 0xFF, pcr->sd30_drive_sel_3v3); return rtsx_pci_send_cmd(pcr, 100); } @@ -110,7 +135,7 @@ static int rts5229_card_power_off(struct rtsx_pcr *pcr, int card) SD_POWER_MASK | PMOS_STRG_MASK, SD_POWER_OFF | PMOS_STRG_400mA); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, - LDO3318_PWR_MASK, 0X00); + LDO3318_PWR_MASK, 0x00); return rtsx_pci_send_cmd(pcr, 100); } @@ -120,7 +145,7 @@ static int rts5229_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) if (voltage == OUTPUT_3V3) { err = rtsx_pci_write_register(pcr, - SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_D); + SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_3v3); if (err < 0) return err; err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); @@ -128,7 +153,7 @@ static int rts5229_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) return err; } else if (voltage == OUTPUT_1V8) { err = rtsx_pci_write_register(pcr, - SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B); + SD30_DRIVE_SEL, 0x07, pcr->sd30_drive_sel_1v8); if (err < 0) return err; err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); @@ -142,6 +167,7 @@ static int rts5229_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) } static const struct pcr_ops rts5229_pcr_ops = { + .fetch_vendor_settings = rts5229_fetch_vendor_settings, .extra_init_hw = rts5229_extra_init_hw, .optimize_phy = rts5229_optimize_phy, .turn_on_led = rts5229_turn_on_led, @@ -221,6 +247,12 @@ void rts5229_init_params(struct rtsx_pcr *pcr) pcr->num_slots = 2; pcr->ops = &rts5229_pcr_ops; + pcr->flags = 0; + pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT; + pcr->sd30_drive_sel_1v8 = DRIVER_TYPE_B; + pcr->sd30_drive_sel_3v3 = DRIVER_TYPE_D; + pcr->aspm_en = ASPM_L1_EN; + pcr->ic_version = rts5229_get_ic_version(pcr); if (pcr->ic_version == IC_VER_C) { pcr->sd_pull_ctl_enable_tbl = rts5229_sd_pull_ctl_enable_tbl2; diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index 15dc848bc081..d5db182f35db 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -34,6 +34,60 @@ static u8 rts5249_get_ic_version(struct rtsx_pcr *pcr) return val & 0x0F; } +static void rts5249_fill_driving(struct rtsx_pcr *pcr, u8 voltage) +{ + u8 driving_3v3[4][3] = { + {0x11, 0x11, 0x11}, + {0x55, 0x55, 0x5C}, + {0x99, 0x99, 0x92}, + {0x99, 0x99, 0x92}, + }; + u8 driving_1v8[4][3] = { + {0x3C, 0x3C, 0x3C}, + {0xB3, 0xB3, 0xB3}, + {0xFE, 0xFE, 0xFE}, + {0xC4, 0xC4, 0xC4}, + }; + u8 (*driving)[3], drive_sel; + + if (voltage == OUTPUT_3V3) { + driving = driving_3v3; + drive_sel = pcr->sd30_drive_sel_3v3; + } else { + driving = driving_1v8; + drive_sel = pcr->sd30_drive_sel_1v8; + } + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL, + 0xFF, driving[drive_sel][0]); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL, + 0xFF, driving[drive_sel][1]); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL, + 0xFF, driving[drive_sel][2]); +} + +static void rts5249_fetch_vendor_settings(struct rtsx_pcr *pcr) +{ + u32 reg; + + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG1, ®); + dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG1, reg); + + if (!rtsx_vendor_setting_valid(reg)) + return; + + pcr->aspm_en = rtsx_reg_to_aspm(reg); + pcr->sd30_drive_sel_1v8 = rtsx_reg_to_sd30_drive_sel_1v8(reg); + pcr->card_drive_sel &= 0x3F; + pcr->card_drive_sel |= rtsx_reg_to_card_drive_sel(reg); + + rtsx_pci_read_config_dword(pcr, PCR_SETTING_REG2, ®); + dev_dbg(&(pcr->pci->dev), "Cfg 0x%x: 0x%x\n", PCR_SETTING_REG2, reg); + pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); + if (rtsx_reg_check_reverse_socket(reg)) + pcr->flags |= PCR_REVERSE_SOCKET; +} + static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) { rtsx_pci_init_cmd(pcr); @@ -45,13 +99,14 @@ static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01); /* LED shine disabled, set initial shine cycle period */ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02); - /* Correct driving */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - SD30_CLK_DRIVE_SEL, 0xFF, 0x99); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - SD30_CMD_DRIVE_SEL, 0xFF, 0x99); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - SD30_DAT_DRIVE_SEL, 0xFF, 0x92); + /* Configure driving */ + rts5249_fill_driving(pcr, OUTPUT_3V3); + if (pcr->flags & PCR_REVERSE_SOCKET) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + AUTOLOAD_CFG_BASE + 3, 0xB0, 0xB0); + else + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + AUTOLOAD_CFG_BASE + 3, 0xB0, 0x80); return rtsx_pci_send_cmd(pcr, 100); } @@ -129,15 +184,11 @@ static int rts5249_card_power_off(struct rtsx_pcr *pcr, int card) static int rts5249_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) { int err; - u8 clk_drive, cmd_drive, dat_drive; if (voltage == OUTPUT_3V3) { err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, 0x4FC0 | 0x24); if (err < 0) return err; - clk_drive = 0x99; - cmd_drive = 0x99; - dat_drive = 0x92; } else if (voltage == OUTPUT_1V8) { err = rtsx_pci_write_phy_register(pcr, PHY_BACR, 0x3C02); if (err < 0) @@ -145,25 +196,18 @@ static int rts5249_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, 0x4C40 | 0x24); if (err < 0) return err; - clk_drive = 0xb3; - cmd_drive = 0xb3; - dat_drive = 0xb3; } else { return -EINVAL; } /* set pad drive */ rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL, - 0xFF, clk_drive); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL, - 0xFF, cmd_drive); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL, - 0xFF, dat_drive); + rts5249_fill_driving(pcr, voltage); return rtsx_pci_send_cmd(pcr, 100); } static const struct pcr_ops rts5249_pcr_ops = { + .fetch_vendor_settings = rts5249_fetch_vendor_settings, .extra_init_hw = rts5249_extra_init_hw, .optimize_phy = rts5249_optimize_phy, .turn_on_led = rts5249_turn_on_led, @@ -233,6 +277,12 @@ void rts5249_init_params(struct rtsx_pcr *pcr) pcr->num_slots = 2; pcr->ops = &rts5249_pcr_ops; + pcr->flags = 0; + pcr->card_drive_sel = RTSX_CARD_DRIVE_DEFAULT; + pcr->sd30_drive_sel_1v8 = CFG_DRIVER_TYPE_C; + pcr->sd30_drive_sel_3v3 = CFG_DRIVER_TYPE_B; + pcr->aspm_en = ASPM_L1_EN; + pcr->ic_version = rts5249_get_ic_version(pcr); pcr->sd_pull_ctl_enable_tbl = rts5249_sd_pull_ctl_enable_tbl; pcr->sd_pull_ctl_disable_tbl = rts5249_sd_pull_ctl_disable_tbl; diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index dd186c4103c1..e06d6b0d55f6 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -73,6 +73,9 @@ void rtsx_pci_start_run(struct rtsx_pcr *pcr) pcr->state = PDEV_STAT_RUN; if (pcr->ops->enable_auto_blink) pcr->ops->enable_auto_blink(pcr); + + if (pcr->aspm_en) + rtsx_pci_write_config_byte(pcr, LCTLR, 0); } mod_delayed_work(system_wq, &pcr->idle_work, msecs_to_jiffies(200)); @@ -717,7 +720,7 @@ int rtsx_pci_card_exclusive_check(struct rtsx_pcr *pcr, int card) [RTSX_MS_CARD] = MS_EXIST }; - if (!pcr->ms_pmos) { + if (!(pcr->flags & PCR_MS_PMOS)) { /* When using single PMOS, accessing card is not permitted * if the existing card is not the designated one. */ @@ -918,6 +921,9 @@ static void rtsx_pci_idle_work(struct work_struct *work) if (pcr->ops->turn_off_led) pcr->ops->turn_off_led(pcr); + if (pcr->aspm_en) + rtsx_pci_write_config_byte(pcr, LCTLR, pcr->aspm_en); + mutex_unlock(&pcr->pcr_mutex); } @@ -956,8 +962,8 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) /* Reset delink mode */ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CHANGE_LINK_STATE, 0x0A, 0); /* Card driving select */ - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DRIVE_SEL, - 0x07, DRIVER_TYPE_D); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DRIVE_SEL, + 0xFF, pcr->card_drive_sel); /* Enable SSC Clock */ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SSC_CTL1, 0xFF, SSC_8X_EN | SSC_SEL_4M); @@ -989,6 +995,8 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) if (err < 0) return err; + rtsx_pci_write_config_byte(pcr, LCTLR, 0); + /* Enable clk_request_n to enable clock power management */ rtsx_pci_write_config_byte(pcr, 0x81, 1); /* Enter L1 when host tx idle */ @@ -1053,6 +1061,18 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) if (!pcr->slots) return -ENOMEM; + if (pcr->ops->fetch_vendor_settings) + pcr->ops->fetch_vendor_settings(pcr); + + dev_dbg(&(pcr->pci->dev), "pcr->aspm_en = 0x%x\n", pcr->aspm_en); + dev_dbg(&(pcr->pci->dev), "pcr->sd30_drive_sel_1v8 = 0x%x\n", + pcr->sd30_drive_sel_1v8); + dev_dbg(&(pcr->pci->dev), "pcr->sd30_drive_sel_3v3 = 0x%x\n", + pcr->sd30_drive_sel_3v3); + dev_dbg(&(pcr->pci->dev), "pcr->card_drive_sel = 0x%x\n", + pcr->card_drive_sel); + dev_dbg(&(pcr->pci->dev), "pcr->flags = 0x%x\n", pcr->flags); + pcr->state = PDEV_STAT_IDLE; err = rtsx_pci_init_hw(pcr); if (err < 0) { diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index c0cac7e8972f..7a1b87a250a5 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -35,4 +35,33 @@ void rts5227_init_params(struct rtsx_pcr *pcr); void rts5249_init_params(struct rtsx_pcr *pcr); void rtl8411b_init_params(struct rtsx_pcr *pcr); +static inline u8 map_sd_drive(int idx) +{ + u8 sd_drive[4] = { + 0x01, /* Type D */ + 0x02, /* Type C */ + 0x05, /* Type A */ + 0x03 /* Type B */ + }; + + return sd_drive[idx]; +} + +#define rtsx_vendor_setting_valid(reg) (!((reg) & 0x1000000)) +#define rts5209_vendor_setting1_valid(reg) (!((reg) & 0x80)) +#define rts5209_vendor_setting2_valid(reg) ((reg) & 0x80) + +#define rtsx_reg_to_aspm(reg) (((reg) >> 28) & 0x03) +#define rtsx_reg_to_sd30_drive_sel_1v8(reg) (((reg) >> 26) & 0x03) +#define rtsx_reg_to_sd30_drive_sel_3v3(reg) (((reg) >> 5) & 0x03) +#define rtsx_reg_to_card_drive_sel(reg) ((((reg) >> 25) & 0x01) << 6) +#define rtsx_reg_check_reverse_socket(reg) ((reg) & 0x4000) +#define rts5209_reg_to_aspm(reg) (((reg) >> 5) & 0x03) +#define rts5209_reg_check_ms_pmos(reg) (!((reg) & 0x08)) +#define rts5209_reg_to_sd30_drive_sel_1v8(reg) (((reg) >> 3) & 0x07) +#define rts5209_reg_to_sd30_drive_sel_3v3(reg) ((reg) & 0x07) +#define rts5209_reg_to_card_drive_sel(reg) ((reg) >> 8) +#define rtl8411_reg_to_sd30_drive_sel_3v3(reg) (((reg) >> 5) & 0x07) +#define rtl8411b_reg_to_sd30_drive_sel_3v3(reg) ((reg) & 0x03) + #endif diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 7a9f7089435d..9cba73703704 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -184,11 +184,26 @@ #define CARD_SHARE_BAROSSA_SD 0x01 #define CARD_SHARE_BAROSSA_MS 0x02 +/* CARD_DRIVE_SEL */ +#define MS_DRIVE_8mA (0x01 << 6) +#define MMC_DRIVE_8mA (0x01 << 4) +#define XD_DRIVE_8mA (0x01 << 2) +#define GPIO_DRIVE_8mA 0x01 +#define RTS5209_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | MMC_DRIVE_8mA |\ + XD_DRIVE_8mA | GPIO_DRIVE_8mA) +#define RTL8411_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | MMC_DRIVE_8mA |\ + XD_DRIVE_8mA) +#define RTSX_CARD_DRIVE_DEFAULT (MS_DRIVE_8mA | GPIO_DRIVE_8mA) + /* SD30_DRIVE_SEL */ #define DRIVER_TYPE_A 0x05 #define DRIVER_TYPE_B 0x03 #define DRIVER_TYPE_C 0x02 #define DRIVER_TYPE_D 0x01 +#define CFG_DRIVER_TYPE_A 0x02 +#define CFG_DRIVER_TYPE_B 0x03 +#define CFG_DRIVER_TYPE_C 0x01 +#define CFG_DRIVER_TYPE_D 0x00 /* FPDCTL */ #define SSC_POWER_DOWN 0x01 @@ -684,6 +699,8 @@ #define DUMMY_REG_RESET_0 0xFE90 +#define AUTOLOAD_CFG_BASE 0xFF00 + /* Memory mapping */ #define SRAM_BASE 0xE600 #define RBUF_BASE 0xF400 @@ -726,6 +743,11 @@ #define PHY_FLD4 0x1E #define PHY_DUM_REG 0x1F +#define LCTLR 0x80 +#define PCR_SETTING_REG1 0x724 +#define PCR_SETTING_REG2 0x814 +#define PCR_SETTING_REG3 0x747 + #define rtsx_pci_init_cmd(pcr) ((pcr)->ci = 0) struct rtsx_pcr; @@ -747,6 +769,7 @@ struct pcr_ops { u8 voltage); unsigned int (*cd_deglitch)(struct rtsx_pcr *pcr); int (*conv_clk_and_div_n)(int clk, int dir); + void (*fetch_vendor_settings)(struct rtsx_pcr *pcr); }; enum PDEV_STAT {PDEV_STAT_IDLE, PDEV_STAT_RUN}; @@ -788,7 +811,6 @@ struct rtsx_pcr { struct completion *finish_me; unsigned int cur_clock; - bool ms_pmos; bool remove_pci; bool msi_en; @@ -806,6 +828,16 @@ struct rtsx_pcr { #define IC_VER_D 3 u8 ic_version; + u8 sd30_drive_sel_1v8; + u8 sd30_drive_sel_3v3; + u8 card_drive_sel; +#define ASPM_L1_EN 0x02 + u8 aspm_en; + +#define PCR_MS_PMOS (1 << 0) +#define PCR_REVERSE_SOCKET (1 << 1) + u32 flags; + const u32 *sd_pull_ctl_enable_tbl; const u32 *sd_pull_ctl_disable_tbl; const u32 *ms_pull_ctl_enable_tbl; -- cgit v1.2.3-71-gd317 From 5947c167d145f32d4475d647a87e2af2699efe45 Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 20 Aug 2013 14:18:52 +0800 Subject: mfd: rtsx: Add shutdown callback in rtsx_pci_driver Some actions to clear power state should be handled in .shutdown callback in rtsx_pci_driver. This patch adopts the following measures to catch this goal: 1. Add a function rtsx_pci_power_off to abstract the common ops in .shutdown and .suspend 2. Add pcr->ops->force_power_down to fulfill the individual action for each reader model Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/rtl8411.c | 7 +++++++ drivers/mfd/rts5209.c | 6 ++++++ drivers/mfd/rts5227.c | 11 +++++++++++ drivers/mfd/rts5229.c | 6 ++++++ drivers/mfd/rts5249.c | 11 +++++++++++ drivers/mfd/rtsx_pcr.c | 43 +++++++++++++++++++++++++++++++++---------- include/linux/mfd/rtsx_pci.h | 13 +++++++++++-- 7 files changed, 85 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c index 5a68c9bdeddf..56cc248392b5 100644 --- a/drivers/mfd/rtl8411.c +++ b/drivers/mfd/rtl8411.c @@ -86,6 +86,11 @@ static void rtl8411b_fetch_vendor_settings(struct rtsx_pcr *pcr) map_sd_drive(rtl8411b_reg_to_sd30_drive_sel_3v3(reg)); } +static void rtl8411_force_power_down(struct rtsx_pcr *pcr) +{ + rtsx_pci_write_register(pcr, FPDCTL, 0x07, 0x07); +} + static int rtl8411_extra_init_hw(struct rtsx_pcr *pcr) { rtsx_pci_init_cmd(pcr); @@ -285,6 +290,7 @@ static const struct pcr_ops rtl8411_pcr_ops = { .switch_output_voltage = rtl8411_switch_output_voltage, .cd_deglitch = rtl8411_cd_deglitch, .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n, + .force_power_down = rtl8411_force_power_down, }; static const struct pcr_ops rtl8411b_pcr_ops = { @@ -300,6 +306,7 @@ static const struct pcr_ops rtl8411b_pcr_ops = { .switch_output_voltage = rtl8411_switch_output_voltage, .cd_deglitch = rtl8411_cd_deglitch, .conv_clk_and_div_n = rtl8411_conv_clk_and_div_n, + .force_power_down = rtl8411_force_power_down, }; /* SD Pull Control Enable: diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c index 2170449bd7e5..c67935efd4f5 100644 --- a/drivers/mfd/rts5209.c +++ b/drivers/mfd/rts5209.c @@ -59,6 +59,11 @@ static void rts5209_fetch_vendor_settings(struct rtsx_pcr *pcr) } } +static void rts5209_force_power_down(struct rtsx_pcr *pcr) +{ + rtsx_pci_write_register(pcr, FPDCTL, 0x07, 0x07); +} + static int rts5209_extra_init_hw(struct rtsx_pcr *pcr) { rtsx_pci_init_cmd(pcr); @@ -197,6 +202,7 @@ static const struct pcr_ops rts5209_pcr_ops = { .switch_output_voltage = rts5209_switch_output_voltage, .cd_deglitch = NULL, .conv_clk_and_div_n = NULL, + .force_power_down = rts5209_force_power_down, }; /* SD Pull Control Enable: diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c index c3181d71fedd..42ebf5c050c1 100644 --- a/drivers/mfd/rts5227.c +++ b/drivers/mfd/rts5227.c @@ -83,6 +83,16 @@ static void rts5227_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->flags |= PCR_REVERSE_SOCKET; } +static void rts5227_force_power_down(struct rtsx_pcr *pcr) +{ + /* Set relink_time to 0 */ + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, 0xFF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, 0xFF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, 0x01, 0); + + rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); +} + static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) { u16 cap; @@ -218,6 +228,7 @@ static const struct pcr_ops rts5227_pcr_ops = { .switch_output_voltage = rts5227_switch_output_voltage, .cd_deglitch = NULL, .conv_clk_and_div_n = NULL, + .force_power_down = rts5227_force_power_down, }; /* SD Pull Control Enable: diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c index 7a1ad6dd2917..a0b695af8561 100644 --- a/drivers/mfd/rts5229.c +++ b/drivers/mfd/rts5229.c @@ -56,6 +56,11 @@ static void rts5229_fetch_vendor_settings(struct rtsx_pcr *pcr) map_sd_drive(rtsx_reg_to_sd30_drive_sel_3v3(reg)); } +static void rts5229_force_power_down(struct rtsx_pcr *pcr) +{ + rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); +} + static int rts5229_extra_init_hw(struct rtsx_pcr *pcr) { rtsx_pci_init_cmd(pcr); @@ -179,6 +184,7 @@ static const struct pcr_ops rts5229_pcr_ops = { .switch_output_voltage = rts5229_switch_output_voltage, .cd_deglitch = NULL, .conv_clk_and_div_n = NULL, + .force_power_down = rts5229_force_power_down, }; /* SD Pull Control Enable: diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index d5db182f35db..79ff212c3e7b 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -88,6 +88,16 @@ static void rts5249_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->flags |= PCR_REVERSE_SOCKET; } +static void rts5249_force_power_down(struct rtsx_pcr *pcr) +{ + /* Set relink_time to 0 */ + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, 0xFF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, 0xFF, 0); + rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, 0x01, 0); + + rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); +} + static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) { rtsx_pci_init_cmd(pcr); @@ -217,6 +227,7 @@ static const struct pcr_ops rts5249_pcr_ops = { .card_power_on = rts5249_card_power_on, .card_power_off = rts5249_card_power_off, .switch_output_voltage = rts5249_switch_output_voltage, + .force_power_down = rts5249_force_power_down, }; /* SD Pull Control Enable: diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index e06d6b0d55f6..97526f1acf96 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -927,6 +927,21 @@ static void rtsx_pci_idle_work(struct work_struct *work) mutex_unlock(&pcr->pcr_mutex); } +static void rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state) +{ + if (pcr->ops->turn_off_led) + pcr->ops->turn_off_led(pcr); + + rtsx_pci_writel(pcr, RTSX_BIER, 0); + pcr->bier = 0; + + rtsx_pci_write_register(pcr, PETXCFG, 0x08, 0x08); + rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, pm_state); + + if (pcr->ops->force_power_down) + pcr->ops->force_power_down(pcr); +} + static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) { int err; @@ -1255,7 +1270,6 @@ static int rtsx_pci_suspend(struct pci_dev *pcidev, pm_message_t state) { struct pcr_handle *handle; struct rtsx_pcr *pcr; - int ret = 0; dev_dbg(&(pcidev->dev), "--> %s\n", __func__); @@ -1267,14 +1281,7 @@ static int rtsx_pci_suspend(struct pci_dev *pcidev, pm_message_t state) mutex_lock(&pcr->pcr_mutex); - if (pcr->ops->turn_off_led) - pcr->ops->turn_off_led(pcr); - - rtsx_pci_writel(pcr, RTSX_BIER, 0); - pcr->bier = 0; - - rtsx_pci_write_register(pcr, PETXCFG, 0x08, 0x08); - rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x02); + rtsx_pci_power_off(pcr, HOST_ENTER_S3); pci_save_state(pcidev); pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0); @@ -1282,7 +1289,7 @@ static int rtsx_pci_suspend(struct pci_dev *pcidev, pm_message_t state) pci_set_power_state(pcidev, pci_choose_state(pcidev, state)); mutex_unlock(&pcr->pcr_mutex); - return ret; + return 0; } static int rtsx_pci_resume(struct pci_dev *pcidev) @@ -1320,10 +1327,25 @@ out: return ret; } +static void rtsx_pci_shutdown(struct pci_dev *pcidev) +{ + struct pcr_handle *handle; + struct rtsx_pcr *pcr; + + dev_dbg(&(pcidev->dev), "--> %s\n", __func__); + + handle = pci_get_drvdata(pcidev); + pcr = handle->pcr; + rtsx_pci_power_off(pcr, HOST_ENTER_S1); + + pci_disable_device(pcidev); +} + #else /* CONFIG_PM */ #define rtsx_pci_suspend NULL #define rtsx_pci_resume NULL +#define rtsx_pci_shutdown NULL #endif /* CONFIG_PM */ @@ -1334,6 +1356,7 @@ static struct pci_driver rtsx_pci_driver = { .remove = rtsx_pci_remove, .suspend = rtsx_pci_suspend, .resume = rtsx_pci_resume, + .shutdown = rtsx_pci_shutdown, }; module_pci_driver(rtsx_pci_driver); diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 9cba73703704..11ab786fdc83 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -25,8 +25,7 @@ #include #include - -#include "rtsx_common.h" +#include #define MAX_RW_REG_CNT 1024 @@ -536,6 +535,10 @@ #define SAMPLE_VAR_CLK0 (0x01 << 4) #define SAMPLE_VAR_CLK1 (0x02 << 4) +/* HOST_SLEEP_STATE */ +#define HOST_ENTER_S1 1 +#define HOST_ENTER_S3 2 + #define MS_CFG 0xFD40 #define MS_TPC 0xFD41 #define MS_TRANS_CFG 0xFD42 @@ -701,6 +704,11 @@ #define AUTOLOAD_CFG_BASE 0xFF00 +#define PM_CTRL1 0xFF44 +#define PM_CTRL2 0xFF45 +#define PM_CTRL3 0xFF46 +#define PM_CTRL4 0xFF47 + /* Memory mapping */ #define SRAM_BASE 0xE600 #define RBUF_BASE 0xF400 @@ -770,6 +778,7 @@ struct pcr_ops { unsigned int (*cd_deglitch)(struct rtsx_pcr *pcr); int (*conv_clk_and_div_n)(int clk, int dir); void (*fetch_vendor_settings)(struct rtsx_pcr *pcr); + void (*force_power_down)(struct rtsx_pcr *pcr); }; enum PDEV_STAT {PDEV_STAT_IDLE, PDEV_STAT_RUN}; -- cgit v1.2.3-71-gd317 From 0ccc0065c90284f283314e52a96e76ae8a8dce0a Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 20 Aug 2013 14:18:54 +0800 Subject: mfd: rtsx: Clear hardware PFM mode in rtl8411b Clear hw_pfm_en to disable hardware PFM mode, to fix a bug that in some situation registers in 0xFDxx domain can't be accessed. Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/rtl8411.c | 2 ++ include/linux/mfd/rtsx_pci.h | 1 + 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c index 56cc248392b5..d183fa002338 100644 --- a/drivers/mfd/rtl8411.c +++ b/drivers/mfd/rtl8411.c @@ -114,6 +114,8 @@ static int rtl8411b_extra_init_hw(struct rtsx_pcr *pcr) 0xFF, pcr->sd30_drive_sel_3v3); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CD_PAD_CTL, CD_DISABLE_MASK | CD_AUTO_DISABLE, CD_ENABLE); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, FUNC_FORCE_CTL, + 0x06, 0x00); return rtsx_pci_send_cmd(pcr, 100); } diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 11ab786fdc83..9a16276bc080 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -687,6 +687,7 @@ #define PME_FORCE_CTL 0xFE56 #define ASPM_FORCE_CTL 0xFE57 #define PM_CLK_FORCE_CTL 0xFE58 +#define FUNC_FORCE_CTL 0xFE59 #define PERST_GLITCH_WIDTH 0xFE5C #define CHANGE_LINK_STATE 0xFE5B #define RESET_LOAD_REG 0xFE5E -- cgit v1.2.3-71-gd317 From eb891c65c9c87e55f728d95bcc514dea337aed12 Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 20 Aug 2013 14:18:55 +0800 Subject: mfd: rtsx: Configure to enter a deeper power-saving mode in S3 Set a bit to enable rts5227 and rts5249 to enter a deeper internal power-saving mode in S3, and recover it after resuming. Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/rtl8411.c | 2 +- drivers/mfd/rts5209.c | 2 +- drivers/mfd/rts5227.c | 6 +++++- drivers/mfd/rts5229.c | 2 +- drivers/mfd/rts5249.c | 6 +++++- drivers/mfd/rtsx_pcr.c | 2 +- include/linux/mfd/rtsx_pci.h | 2 +- 7 files changed, 15 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c index d183fa002338..37367fb1c11b 100644 --- a/drivers/mfd/rtl8411.c +++ b/drivers/mfd/rtl8411.c @@ -86,7 +86,7 @@ static void rtl8411b_fetch_vendor_settings(struct rtsx_pcr *pcr) map_sd_drive(rtl8411b_reg_to_sd30_drive_sel_3v3(reg)); } -static void rtl8411_force_power_down(struct rtsx_pcr *pcr) +static void rtl8411_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) { rtsx_pci_write_register(pcr, FPDCTL, 0x07, 0x07); } diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c index 03a15f779ebb..ef6a59fe37a4 100644 --- a/drivers/mfd/rts5209.c +++ b/drivers/mfd/rts5209.c @@ -59,7 +59,7 @@ static void rts5209_fetch_vendor_settings(struct rtsx_pcr *pcr) } } -static void rts5209_force_power_down(struct rtsx_pcr *pcr) +static void rts5209_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) { rtsx_pci_write_register(pcr, FPDCTL, 0x07, 0x07); } diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c index 724ce4c54523..c72abd6b9c81 100644 --- a/drivers/mfd/rts5227.c +++ b/drivers/mfd/rts5227.c @@ -83,13 +83,16 @@ static void rts5227_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->flags |= PCR_REVERSE_SOCKET; } -static void rts5227_force_power_down(struct rtsx_pcr *pcr) +static void rts5227_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) { /* Set relink_time to 0 */ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, 0xFF, 0); rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, 0xFF, 0); rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, 0x01, 0); + if (pm_state == HOST_ENTER_S3) + rtsx_pci_write_register(pcr, PM_CTRL3, 0x10, 0x10); + rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); } @@ -123,6 +126,7 @@ static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) else rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, AUTOLOAD_CFG_BASE + 3, 0xB8, 0x88); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x10, 0x00); return rtsx_pci_send_cmd(pcr, 100); } diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c index e8261d7436a8..afb0f24bac71 100644 --- a/drivers/mfd/rts5229.c +++ b/drivers/mfd/rts5229.c @@ -56,7 +56,7 @@ static void rts5229_fetch_vendor_settings(struct rtsx_pcr *pcr) map_sd_drive(rtsx_reg_to_sd30_drive_sel_3v3(reg)); } -static void rts5229_force_power_down(struct rtsx_pcr *pcr) +static void rts5229_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) { rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); } diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index c5e54d7cf528..384b30b432bd 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -88,13 +88,16 @@ static void rts5249_fetch_vendor_settings(struct rtsx_pcr *pcr) pcr->flags |= PCR_REVERSE_SOCKET; } -static void rts5249_force_power_down(struct rtsx_pcr *pcr) +static void rts5249_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) { /* Set relink_time to 0 */ rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 1, 0xFF, 0); rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 2, 0xFF, 0); rtsx_pci_write_register(pcr, AUTOLOAD_CFG_BASE + 3, 0x01, 0); + if (pm_state == HOST_ENTER_S3) + rtsx_pci_write_register(pcr, PM_CTRL3, 0x10, 0x10); + rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); } @@ -119,6 +122,7 @@ static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) else rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, AUTOLOAD_CFG_BASE + 3, 0xB0, 0x80); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PM_CTRL3, 0x10, 0x00); return rtsx_pci_send_cmd(pcr, 100); } diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index ffd393c06d34..29932a019fdf 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -939,7 +939,7 @@ static void rtsx_pci_power_off(struct rtsx_pcr *pcr, u8 pm_state) rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, pm_state); if (pcr->ops->force_power_down) - pcr->ops->force_power_down(pcr); + pcr->ops->force_power_down(pcr, pm_state); } static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 9a16276bc080..dd0bd5806adc 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -779,7 +779,7 @@ struct pcr_ops { unsigned int (*cd_deglitch)(struct rtsx_pcr *pcr); int (*conv_clk_and_div_n)(int clk, int dir); void (*fetch_vendor_settings)(struct rtsx_pcr *pcr); - void (*force_power_down)(struct rtsx_pcr *pcr); + void (*force_power_down)(struct rtsx_pcr *pcr, u8 pm_state); }; enum PDEV_STAT {PDEV_STAT_IDLE, PDEV_STAT_RUN}; -- cgit v1.2.3-71-gd317 From 09fd86780bb4603df1886e17127f5beb97f70ffb Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Tue, 20 Aug 2013 14:18:56 +0800 Subject: mfd: rtsx: Copyright modifications Update copyright date, remove author address and add Roger Tseng. Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz --- drivers/mfd/rtl8411.c | 4 ++-- drivers/mfd/rts5209.c | 3 +-- drivers/mfd/rts5227.c | 5 +---- drivers/mfd/rts5229.c | 3 +-- drivers/mfd/rts5249.c | 1 - drivers/mfd/rtsx_pcr.c | 3 +-- drivers/mfd/rtsx_pcr.h | 3 +-- include/linux/mfd/rtsx_common.h | 3 +-- include/linux/mfd/rtsx_pci.h | 3 +-- 9 files changed, 9 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/mfd/rtl8411.c b/drivers/mfd/rtl8411.c index 37367fb1c11b..e4c1833154ea 100644 --- a/drivers/mfd/rtl8411.c +++ b/drivers/mfd/rtl8411.c @@ -1,6 +1,6 @@ /* Driver for Realtek PCI-Express card reader * - * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. * * 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 @@ -17,7 +17,7 @@ * * Author: * Wei WANG - * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + * Roger Tseng */ #include diff --git a/drivers/mfd/rts5209.c b/drivers/mfd/rts5209.c index ef6a59fe37a4..4026e1fcb0a6 100644 --- a/drivers/mfd/rts5209.c +++ b/drivers/mfd/rts5209.c @@ -1,6 +1,6 @@ /* Driver for Realtek PCI-Express card reader * - * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. * * 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 @@ -17,7 +17,6 @@ * * Author: * Wei WANG - * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China */ #include diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c index c72abd6b9c81..d7cae82720a4 100644 --- a/drivers/mfd/rts5227.c +++ b/drivers/mfd/rts5227.c @@ -1,6 +1,6 @@ /* Driver for Realtek PCI-Express card reader * - * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. * * 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 @@ -17,10 +17,7 @@ * * Author: * Wei WANG - * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China - * * Roger Tseng - * No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan */ #include diff --git a/drivers/mfd/rts5229.c b/drivers/mfd/rts5229.c index afb0f24bac71..620e7fa9e0df 100644 --- a/drivers/mfd/rts5229.c +++ b/drivers/mfd/rts5229.c @@ -1,6 +1,6 @@ /* Driver for Realtek PCI-Express card reader * - * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. * * 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 @@ -17,7 +17,6 @@ * * Author: * Wei WANG - * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China */ #include diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index 384b30b432bd..ea90f8fb92ae 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -17,7 +17,6 @@ * * Author: * Wei WANG - * No. 128, West Shenhu Road, Suzhou Industry Park, Suzhou, China */ #include diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 29932a019fdf..e6ae7720f9e1 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -1,6 +1,6 @@ /* Driver for Realtek PCI-Express card reader * - * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. * * 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 @@ -17,7 +17,6 @@ * * Author: * Wei WANG - * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China */ #include diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index 7a1b87a250a5..947e79b05ceb 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -1,6 +1,6 @@ /* Driver for Realtek PCI-Express card reader * - * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. * * 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 @@ -17,7 +17,6 @@ * * Author: * Wei WANG - * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China */ #ifndef __RTSX_PCR_H diff --git a/include/linux/mfd/rtsx_common.h b/include/linux/mfd/rtsx_common.h index 2b13970596f5..443176ee1ab0 100644 --- a/include/linux/mfd/rtsx_common.h +++ b/include/linux/mfd/rtsx_common.h @@ -1,6 +1,6 @@ /* Driver for Realtek driver-based card reader * - * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. * * 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 @@ -17,7 +17,6 @@ * * Author: * Wei WANG - * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China */ #ifndef __RTSX_COMMON_H diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index dd0bd5806adc..daefca1bafb3 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -1,6 +1,6 @@ /* Driver for Realtek PCI-Express card reader * - * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. * * 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 @@ -17,7 +17,6 @@ * * Author: * Wei WANG - * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China */ #ifndef __RTSX_PCI_H -- cgit v1.2.3-71-gd317 From 741a509f34d8d702f70d0ad99b8152c57d76961e Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 19 Aug 2013 17:05:55 +0200 Subject: ASoC: core: Generic ac97 link reset functions This patch adds generic ac97 reset functions using pincontrol and gpio parsed from devicetree. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/soc-ac97link.txt | 28 ++++ include/sound/soc.h | 2 + sound/soc/soc-core.c | 153 +++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/soc-ac97link.txt (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/soc-ac97link.txt b/Documentation/devicetree/bindings/sound/soc-ac97link.txt new file mode 100644 index 000000000000..80152a87f239 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/soc-ac97link.txt @@ -0,0 +1,28 @@ +AC97 link bindings + +These bindings can be included within any other device node. + +Required properties: + - pinctrl-names: Has to contain following states to setup the correct + pinmuxing for the used gpios: + "ac97-running": AC97-link is active + "ac97-reset": AC97-link reset state + "ac97-warm-reset": AC97-link warm reset state + - ac97-gpios: List of gpio phandles with args in the order ac97-sync, + ac97-sdata, ac97-reset + + +Example: + +ssi { + ... + + pinctrl-names = "default", "ac97-running", "ac97-reset", "ac97-warm-reset"; + pinctrl-0 = <&ac97link_running>; + pinctrl-1 = <&ac97link_running>; + pinctrl-2 = <&ac97link_reset>; + pinctrl-3 = <&ac97link_warm_reset>; + ac97-gpios = <&gpio3 20 0 &gpio3 22 0 &gpio3 28 0>; + + ... +}; diff --git a/include/sound/soc.h b/include/sound/soc.h index 6eabee7ec15a..c0ac3bc7b7f4 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -468,6 +468,8 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, void snd_soc_free_ac97_codec(struct snd_soc_codec *codec); int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); +int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, + struct platform_device *pdev); /* *Controls diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d82ee386eab5..b5c91f9aa160 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -30,9 +30,12 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -69,6 +72,16 @@ static int pmdown_time = 5000; module_param(pmdown_time, int, 0); MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)"); +struct snd_ac97_reset_cfg { + struct pinctrl *pctl; + struct pinctrl_state *pstate_reset; + struct pinctrl_state *pstate_warm_reset; + struct pinctrl_state *pstate_run; + int gpio_sdata; + int gpio_sync; + int gpio_reset; +}; + /* returns the minimum number of bytes needed to represent * a particular given value */ static int min_bytes_needed(unsigned long val) @@ -2080,6 +2093,117 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); +static struct snd_ac97_reset_cfg snd_ac97_rst_cfg; + +static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97) +{ + struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1); + + udelay(10); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); + msleep(2); +} + +static void snd_soc_ac97_reset(struct snd_ac97 *ac97) +{ + struct pinctrl *pctl = snd_ac97_rst_cfg.pctl; + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0); + gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0); + gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0); + + udelay(10); + + gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1); + + pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run); + msleep(2); +} + +static int snd_soc_ac97_parse_pinctl(struct device *dev, + struct snd_ac97_reset_cfg *cfg) +{ + struct pinctrl *p; + struct pinctrl_state *state; + int gpio; + int ret; + + p = devm_pinctrl_get(dev); + if (IS_ERR(p)) { + dev_err(dev, "Failed to get pinctrl\n"); + return PTR_RET(p); + } + cfg->pctl = p; + + state = pinctrl_lookup_state(p, "ac97-reset"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-reset\n"); + return PTR_RET(state); + } + cfg->pstate_reset = state; + + state = pinctrl_lookup_state(p, "ac97-warm-reset"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n"); + return PTR_RET(state); + } + cfg->pstate_warm_reset = state; + + state = pinctrl_lookup_state(p, "ac97-running"); + if (IS_ERR(state)) { + dev_err(dev, "Can't find pinctrl state ac97-running\n"); + return PTR_RET(state); + } + cfg->pstate_run = state; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-sync gpio\n"); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link sync"); + if (ret) { + dev_err(dev, "Failed requesting ac97-sync gpio\n"); + return ret; + } + cfg->gpio_sync = gpio; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link sdata"); + if (ret) { + dev_err(dev, "Failed requesting ac97-sdata gpio\n"); + return ret; + } + cfg->gpio_sdata = gpio; + + gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2); + if (gpio < 0) { + dev_err(dev, "Can't find ac97-reset gpio\n"); + return gpio; + } + ret = devm_gpio_request(dev, gpio, "AC97 link reset"); + if (ret) { + dev_err(dev, "Failed requesting ac97-reset gpio\n"); + return ret; + } + cfg->gpio_reset = gpio; + + return 0; +} + struct snd_ac97_bus_ops *soc_ac97_ops; EXPORT_SYMBOL_GPL(soc_ac97_ops); @@ -2097,6 +2221,35 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops) } EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops); +/** + * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions + * + * This function sets the reset and warm_reset properties of ops and parses + * the device node of pdev to get pinctrl states and gpio numbers to use. + */ +int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_ac97_reset_cfg cfg; + int ret; + + ret = snd_soc_ac97_parse_pinctl(dev, &cfg); + if (ret) + return ret; + + ret = snd_soc_set_ac97_ops(ops); + if (ret) + return ret; + + ops->warm_reset = snd_soc_ac97_warm_reset; + ops->reset = snd_soc_ac97_reset; + + snd_ac97_rst_cfg = cfg; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset); + /** * snd_soc_free_ac97_codec - free AC97 codec device * @codec: audio codec -- cgit v1.2.3-71-gd317 From 5a581b367b5df0531265311fc681c2abd377e5e6 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sat, 27 Jul 2013 03:53:54 -0700 Subject: jiffies: Avoid undefined behavior from signed overflow According to the C standard 3.4.3p3, overflow of a signed integer results in undefined behavior. This commit therefore changes the definitions of time_after(), time_after_eq(), time_after64(), and time_after_eq64() to avoid this undefined behavior. The trick is that the subtraction is done using unsigned arithmetic, which according to 6.2.5p9 cannot overflow because it is defined as modulo arithmetic. This has the added (though admittedly quite small) benefit of shortening four lines of code by four characters each. Note that the C standard considers the cast from unsigned to signed to be implementation-defined, see 6.3.1.3p3. However, on a two's-complement system, an implementation that defines anything other than a reinterpretation of the bits is free to come to me, and I will be happy to act as a witness for its being committed to an insane asylum. (Although I have nothing against saturating arithmetic or signals in some cases, these things really should not be the default when compiling an operating-system kernel.) Signed-off-by: Paul E. McKenney Cc: John Stultz Cc: "David S. Miller" Cc: Arnd Bergmann Cc: Ingo Molnar Cc: Linus Torvalds Cc: Eric Dumazet Cc: Kevin Easton [ paulmck: Included time_after64() and time_after_eq64(), as suggested by Eric Dumazet, also fixed commit message.] Reviewed-by: Josh Triplett --- include/linux/jiffies.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index 97ba4e78a37e..d235e88cfd7c 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -101,13 +101,13 @@ static inline u64 get_jiffies_64(void) #define time_after(a,b) \ (typecheck(unsigned long, a) && \ typecheck(unsigned long, b) && \ - ((long)(b) - (long)(a) < 0)) + ((long)((b) - (a)) < 0)) #define time_before(a,b) time_after(b,a) #define time_after_eq(a,b) \ (typecheck(unsigned long, a) && \ typecheck(unsigned long, b) && \ - ((long)(a) - (long)(b) >= 0)) + ((long)((a) - (b)) >= 0)) #define time_before_eq(a,b) time_after_eq(b,a) /* @@ -130,13 +130,13 @@ static inline u64 get_jiffies_64(void) #define time_after64(a,b) \ (typecheck(__u64, a) && \ typecheck(__u64, b) && \ - ((__s64)(b) - (__s64)(a) < 0)) + ((__s64)((b) - (a)) < 0)) #define time_before64(a,b) time_after64(b,a) #define time_after_eq64(a,b) \ (typecheck(__u64, a) && \ typecheck(__u64, b) && \ - ((__s64)(a) - (__s64)(b) >= 0)) + ((__s64)((a) - (b)) >= 0)) #define time_before_eq64(a,b) time_after_eq64(b,a) #define time_in_range64(a, b, c) \ -- cgit v1.2.3-71-gd317 From af30cb446dd5f4ad5b93d7d4188c49a864c0d643 Mon Sep 17 00:00:00 2001 From: Chandra Seetharaman Date: Tue, 6 Aug 2013 17:27:07 -0500 Subject: quota: Add a new quotactl command Q_XGETQSTATV XFS now supports three types of quotas (user, group and project). Current version of Q_XGETSTAT has support for only two types of quotas. In order to support three types of quotas, the interface, specifically struct fs_quota_stat, need to be expanded. Current version of fs_quota_stat does not allow expansion without breaking backward compatibility. So, a quotactl command and new fs_quota_stat structure need to be added. This patch adds a new command Q_XGETQSTATV to quotactl() which takes a new data structure fs_quota_statv. This new data structure provides support for future expansion and backward compatibility. Callers of the new quotactl command have to set the version of the data structure being passed, and kernel will fill as much data as requested. If the kernel does not support the user-space provided version, EINVAL will be returned. User-space can reduce the version number and call the same quotactl again. Signed-off-by: Chandra Seetharaman Reviewed-by: Jan Kara Reviewed-by: Rich Johnston Signed-off-by: Ben Myers [v2: Applied rjohnston's suggestions as per Chandra's request. -bpm] --- fs/quota/quota.c | 29 ++++++++++++++++++++++++++ include/linux/quota.h | 1 + include/uapi/linux/dqblk_xfs.h | 47 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) (limited to 'include') diff --git a/fs/quota/quota.c b/fs/quota/quota.c index c7314f1771f5..dea86e8967ee 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -27,6 +27,7 @@ static int check_quotactl_permission(struct super_block *sb, int type, int cmd, case Q_SYNC: case Q_GETINFO: case Q_XGETQSTAT: + case Q_XGETQSTATV: case Q_XQUOTASYNC: break; /* allow to query information for dquots we "own" */ @@ -217,6 +218,31 @@ static int quota_getxstate(struct super_block *sb, void __user *addr) return ret; } +static int quota_getxstatev(struct super_block *sb, void __user *addr) +{ + struct fs_quota_statv fqs; + int ret; + + if (!sb->s_qcop->get_xstatev) + return -ENOSYS; + + memset(&fqs, 0, sizeof(fqs)); + if (copy_from_user(&fqs, addr, 1)) /* Just read qs_version */ + return -EFAULT; + + /* If this kernel doesn't support user specified version, fail */ + switch (fqs.qs_version) { + case FS_QSTATV_VERSION1: + break; + default: + return -EINVAL; + } + ret = sb->s_qcop->get_xstatev(sb, &fqs); + if (!ret && copy_to_user(addr, &fqs, sizeof(fqs))) + return -EFAULT; + return ret; +} + static int quota_setxquota(struct super_block *sb, int type, qid_t id, void __user *addr) { @@ -293,6 +319,8 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, return quota_setxstate(sb, cmd, addr); case Q_XGETQSTAT: return quota_getxstate(sb, addr); + case Q_XGETQSTATV: + return quota_getxstatev(sb, addr); case Q_XSETQLIM: return quota_setxquota(sb, type, id, addr); case Q_XGETQUOTA: @@ -317,6 +345,7 @@ static int quotactl_cmd_write(int cmd) case Q_GETINFO: case Q_SYNC: case Q_XGETQSTAT: + case Q_XGETQSTATV: case Q_XGETQUOTA: case Q_XQUOTASYNC: return 0; diff --git a/include/linux/quota.h b/include/linux/quota.h index d13371134c59..cc7494a35429 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -328,6 +328,7 @@ struct quotactl_ops { int (*set_dqblk)(struct super_block *, struct kqid, struct fs_disk_quota *); int (*get_xstate)(struct super_block *, struct fs_quota_stat *); int (*set_xstate)(struct super_block *, unsigned int, int); + int (*get_xstatev)(struct super_block *, struct fs_quota_statv *); }; struct quota_format_type { diff --git a/include/uapi/linux/dqblk_xfs.h b/include/uapi/linux/dqblk_xfs.h index 86552807aed9..dcd75cc26196 100644 --- a/include/uapi/linux/dqblk_xfs.h +++ b/include/uapi/linux/dqblk_xfs.h @@ -38,6 +38,7 @@ #define Q_XGETQSTAT XQM_CMD(5) /* get quota subsystem status */ #define Q_XQUOTARM XQM_CMD(6) /* free disk space used by dquots */ #define Q_XQUOTASYNC XQM_CMD(7) /* delalloc flush, updates dquots */ +#define Q_XGETQSTATV XQM_CMD(8) /* newer version of get quota */ /* * fs_disk_quota structure: @@ -163,4 +164,50 @@ typedef struct fs_quota_stat { __u16 qs_iwarnlimit; /* limit for num warnings */ } fs_quota_stat_t; +/* + * fs_quota_statv is used by Q_XGETQSTATV for a given file system. It provides + * a centralized way to get meta information about the quota subsystem. eg. + * space taken up for user, group, and project quotas, number of dquots + * currently incore. + * + * This version has proper versioning support with appropriate padding for + * future expansions, and ability to expand for future without creating any + * backward compatibility issues. + * + * Q_XGETQSTATV uses the passed in value of the requested version via + * fs_quota_statv.qs_version to determine the return data layout of + * fs_quota_statv. The kernel will fill the data fields relevant to that + * version. + * + * If kernel does not support user space caller specified version, EINVAL will + * be returned. User space caller can then reduce the version number and retry + * the same command. + */ +#define FS_QSTATV_VERSION1 1 /* fs_quota_statv.qs_version */ +/* + * Some basic information about 'quota files' for Q_XGETQSTATV command + */ +struct fs_qfilestatv { + __u64 qfs_ino; /* inode number */ + __u64 qfs_nblks; /* number of BBs 512-byte-blks */ + __u32 qfs_nextents; /* number of extents */ + __u32 qfs_pad; /* pad for 8-byte alignment */ +}; + +struct fs_quota_statv { + __s8 qs_version; /* version for future changes */ + __u8 qs_pad1; /* pad for 16bit alignment */ + __u16 qs_flags; /* FS_QUOTA_.* flags */ + __u32 qs_incoredqs; /* number of dquots incore */ + struct fs_qfilestatv qs_uquota; /* user quota information */ + struct fs_qfilestatv qs_gquota; /* group quota information */ + struct fs_qfilestatv qs_pquota; /* project quota information */ + __s32 qs_btimelimit; /* limit for blks timer */ + __s32 qs_itimelimit; /* limit for inodes timer */ + __s32 qs_rtbtimelimit;/* limit for rt blks timer */ + __u16 qs_bwarnlimit; /* limit for num warnings */ + __u16 qs_iwarnlimit; /* limit for num warnings */ + __u64 qs_pad2[8]; /* for future proofing */ +}; + #endif /* _LINUX_DQBLK_XFS_H */ -- cgit v1.2.3-71-gd317 From 6853152689d47b4a1eb9f38a25a18f3f8b7a60de Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 20 Aug 2013 16:48:54 -0700 Subject: sysfs.h: fix __BIN_ATTR_RW() __BIN_ATTR_RW() wasn't passing in the _size field. As it would break the build if this macro was ever used, it's obvious no one had ever tried to use it before. Fix it so that it can be used. Signed-off-by: Greg Kroah-Hartman --- include/linux/sysfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 9e8a9b555ad6..0f04958623d7 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -153,7 +153,7 @@ struct bin_attribute { #define __BIN_ATTR_RW(_name, _size) __BIN_ATTR(_name, \ (S_IWUSR | S_IRUGO), _name##_read, \ - _name##_write) + _name##_write, _size) #define __BIN_ATTR_NULL __ATTR_NULL -- cgit v1.2.3-71-gd317 From 8d3214c4e8c8be6efd8ec7a172239ebbd4deb04b Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sun, 18 Aug 2013 03:13:26 +0400 Subject: sh_eth: remove 'register_type' field from 'struct sh_eth_plat_data' Now that the 'register_type' field of the 'sh_eth' driver's platform data is not used by the driver anymore, it's time to remove it and its initializers from the SH platform code. Also move *enum* declaring values for this field from to the local driver's header file as they're only needed by the driver itself now... Signed-off-by: Sergei Shtylyov Signed-off-by: David S. Miller --- arch/arm/mach-shmobile/board-armadillo800eva.c | 1 - arch/arm/mach-shmobile/board-bockw.c | 1 - arch/sh/boards/board-espt.c | 1 - arch/sh/boards/board-sh7757lcr.c | 4 ---- arch/sh/boards/mach-ecovec24/setup.c | 1 - arch/sh/boards/mach-se/7724/setup.c | 1 - arch/sh/boards/mach-sh7763rdp/setup.c | 1 - arch/sh/kernel/cpu/sh2/setup-sh7619.c | 1 - drivers/net/ethernet/renesas/sh_eth.h | 7 +++++++ include/linux/sh_eth.h | 7 ------- 10 files changed, 7 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c index c5be60d85e4b..3a6ffa250fb1 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva.c @@ -358,7 +358,6 @@ static struct platform_device usbhsf_device = { static struct sh_eth_plat_data sh_eth_platdata = { .phy = 0x00, /* LAN8710A */ .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_GIGABIT, .phy_interface = PHY_INTERFACE_MODE_MII, }; diff --git a/arch/arm/mach-shmobile/board-bockw.c b/arch/arm/mach-shmobile/board-bockw.c index 3354a85c90f7..fa8885b2d5a5 100644 --- a/arch/arm/mach-shmobile/board-bockw.c +++ b/arch/arm/mach-shmobile/board-bockw.c @@ -89,7 +89,6 @@ static struct sh_mobile_sdhi_info sdhi0_info = { static struct sh_eth_plat_data ether_platform_data __initdata = { .phy = 0x01, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_FAST_RCAR, .phy_interface = PHY_INTERFACE_MODE_RMII, /* * Although the LINK signal is available on the board, it's connected to diff --git a/arch/sh/boards/board-espt.c b/arch/sh/boards/board-espt.c index 4d94dff9015c..7291e2f11a47 100644 --- a/arch/sh/boards/board-espt.c +++ b/arch/sh/boards/board-espt.c @@ -80,7 +80,6 @@ static struct resource sh_eth_resources[] = { static struct sh_eth_plat_data sh7763_eth_pdata = { .phy = 0, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_GIGABIT, .phy_interface = PHY_INTERFACE_MODE_MII, }; diff --git a/arch/sh/boards/board-sh7757lcr.c b/arch/sh/boards/board-sh7757lcr.c index 4f114d1cd019..25c5a932f9fe 100644 --- a/arch/sh/boards/board-sh7757lcr.c +++ b/arch/sh/boards/board-sh7757lcr.c @@ -77,7 +77,6 @@ static struct resource sh_eth0_resources[] = { static struct sh_eth_plat_data sh7757_eth0_pdata = { .phy = 1, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_FAST_SH4, .set_mdio_gate = sh7757_eth_set_mdio_gate, }; @@ -106,7 +105,6 @@ static struct resource sh_eth1_resources[] = { static struct sh_eth_plat_data sh7757_eth1_pdata = { .phy = 1, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_FAST_SH4, .set_mdio_gate = sh7757_eth_set_mdio_gate, }; @@ -151,7 +149,6 @@ static struct resource sh_eth_giga0_resources[] = { static struct sh_eth_plat_data sh7757_eth_giga0_pdata = { .phy = 18, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_GIGABIT, .set_mdio_gate = sh7757_eth_giga_set_mdio_gate, .phy_interface = PHY_INTERFACE_MODE_RGMII_ID, }; @@ -186,7 +183,6 @@ static struct resource sh_eth_giga1_resources[] = { static struct sh_eth_plat_data sh7757_eth_giga1_pdata = { .phy = 19, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_GIGABIT, .set_mdio_gate = sh7757_eth_giga_set_mdio_gate, .phy_interface = PHY_INTERFACE_MODE_RGMII_ID, }; diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 61fade0ffa96..a4f630f04ea3 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -159,7 +159,6 @@ static struct resource sh_eth_resources[] = { static struct sh_eth_plat_data sh_eth_plat = { .phy = 0x1f, /* SMSC LAN8700 */ .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_FAST_SH4, .phy_interface = PHY_INTERFACE_MODE_MII, .ether_link_active_low = 1 }; diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index aed4ca9ee1f5..e96e053f2609 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -377,7 +377,6 @@ static struct resource sh_eth_resources[] = { static struct sh_eth_plat_data sh_eth_plat = { .phy = 0x1f, /* SMSC LAN8187 */ .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_FAST_SH4, .phy_interace = PHY_INTERFACE_MODE_MII, }; diff --git a/arch/sh/boards/mach-sh7763rdp/setup.c b/arch/sh/boards/mach-sh7763rdp/setup.c index 50ba481fa240..2c8fb04685d4 100644 --- a/arch/sh/boards/mach-sh7763rdp/setup.c +++ b/arch/sh/boards/mach-sh7763rdp/setup.c @@ -88,7 +88,6 @@ static struct resource sh_eth_resources[] = { static struct sh_eth_plat_data sh7763_eth_pdata = { .phy = 1, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_GIGABIT, .phy_interface = PHY_INTERFACE_MODE_MII, }; diff --git a/arch/sh/kernel/cpu/sh2/setup-sh7619.c b/arch/sh/kernel/cpu/sh2/setup-sh7619.c index bbadd4820336..19472817e274 100644 --- a/arch/sh/kernel/cpu/sh2/setup-sh7619.c +++ b/arch/sh/kernel/cpu/sh2/setup-sh7619.c @@ -114,7 +114,6 @@ static struct platform_device scif2_device = { static struct sh_eth_plat_data eth_platform_data = { .phy = 1, .edmac_endian = EDMAC_LITTLE_ENDIAN, - .register_type = SH_ETH_REG_FAST_SH3_SH2, .phy_interace = PHY_INTERFACE_MODE_MII, }; diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 1e7852810c8d..a0db02c63b11 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -157,6 +157,13 @@ enum { SH_ETH_MAX_REGISTER_OFFSET, }; +enum { + SH_ETH_REG_GIGABIT, + SH_ETH_REG_FAST_RCAR, + SH_ETH_REG_FAST_SH4, + SH_ETH_REG_FAST_SH3_SH2 +}; + /* Driver's parameters */ #if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) #define SH4_SKB_RX_ALIGN 32 diff --git a/include/linux/sh_eth.h b/include/linux/sh_eth.h index 6205eeba392b..90b5e30c2f22 100644 --- a/include/linux/sh_eth.h +++ b/include/linux/sh_eth.h @@ -5,17 +5,10 @@ #include enum {EDMAC_LITTLE_ENDIAN, EDMAC_BIG_ENDIAN}; -enum { - SH_ETH_REG_GIGABIT, - SH_ETH_REG_FAST_RCAR, - SH_ETH_REG_FAST_SH4, - SH_ETH_REG_FAST_SH3_SH2 -}; struct sh_eth_plat_data { int phy; int edmac_endian; - int register_type; phy_interface_t phy_interface; void (*set_mdio_gate)(void *addr); -- cgit v1.2.3-71-gd317 From f51607ac8d4d09b59faf7c6d718f413f537a7b34 Mon Sep 17 00:00:00 2001 From: "Lespiau, Damien" Date: Tue, 20 Aug 2013 00:53:03 +0100 Subject: drm: Remove stale prototypes A few prototypes have been left in the headers, their function friends long gone. Signed-off-by: Damien Lespiau Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- include/drm/drmP.h | 5 ----- include/drm/drm_crtc.h | 11 ----------- 2 files changed, 16 deletions(-) (limited to 'include') diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 39911dca359b..046a7db43a6c 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1371,7 +1371,6 @@ extern int drm_irq_uninstall(struct drm_device *dev); extern int drm_vblank_init(struct drm_device *dev, int num_crtcs); extern int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *filp); -extern int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq); extern u32 drm_vblank_count(struct drm_device *dev, int crtc); extern u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, struct timeval *vblanktime); @@ -1491,10 +1490,6 @@ void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle); void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf); -int drm_prime_add_dma_buf(struct drm_device *dev, struct drm_gem_object *obj); -int drm_prime_lookup_obj(struct drm_device *dev, struct dma_buf *buf, - struct drm_gem_object **obj); - #if DRM_DEBUG_CODE extern int drm_vma_info(struct seq_file *m, void *data); #endif diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 45f133228553..eb40a967fc65 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -948,9 +948,6 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj, extern int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *value); -extern struct drm_display_mode *drm_crtc_mode_create(struct drm_device *dev); -extern void drm_framebuffer_set_object(struct drm_device *dev, - unsigned long handle); extern int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, const struct drm_framebuffer_funcs *funcs); @@ -961,10 +958,6 @@ extern void drm_framebuffer_reference(struct drm_framebuffer *fb); extern void drm_framebuffer_remove(struct drm_framebuffer *fb); extern void drm_framebuffer_cleanup(struct drm_framebuffer *fb); extern void drm_framebuffer_unregister_private(struct drm_framebuffer *fb); -extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc); -extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); -extern void drm_crtc_probe_connector_modes(struct drm_device *dev, int maxX, int maxY); -extern bool drm_crtc_in_use(struct drm_crtc *crtc); extern void drm_object_attach_property(struct drm_mode_object *obj, struct drm_property *property, @@ -1039,10 +1032,6 @@ extern int drm_mode_getblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mode_connector_property_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int drm_mode_hotplug_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv); -extern int drm_mode_replacefb(struct drm_device *dev, - void *data, struct drm_file *file_priv); extern int drm_mode_getencoder(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mode_gamma_get_ioctl(struct drm_device *dev, -- cgit v1.2.3-71-gd317 From ddecb10cf402a8325579f298fd4986a90f33496b Mon Sep 17 00:00:00 2001 From: "Lespiau, Damien" Date: Tue, 20 Aug 2013 00:53:04 +0100 Subject: drm: Remove drm_mode_create_dithering_property() This was last used by nouveau, replaced by a driver-specific property in: commit de69185573586302ada2e59ba41835df36986277 Author: Ben Skeggs Date: Mon Oct 17 12:23:41 2011 +1000 drm/nouveau: improve dithering properties, and implement proper auto mode Signed-off-by: Damien Lespiau Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 31 ------------------------------- include/drm/drm_crtc.h | 1 - 2 files changed, 32 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index a6917645fb4a..ffb791f95d45 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -125,13 +125,6 @@ static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = { DRM_MODE_SCALE_ASPECT, "Full aspect" }, }; -static const struct drm_prop_enum_list drm_dithering_mode_enum_list[] = -{ - { DRM_MODE_DITHERING_OFF, "Off" }, - { DRM_MODE_DITHERING_ON, "On" }, - { DRM_MODE_DITHERING_AUTO, "Automatic" }, -}; - /* * Non-global properties, but "required" for certain connectors. */ @@ -1160,30 +1153,6 @@ int drm_mode_create_scaling_mode_property(struct drm_device *dev) } EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); -/** - * drm_mode_create_dithering_property - create dithering property - * @dev: DRM device - * - * Called by a driver the first time it's needed, must be attached to desired - * connectors. - */ -int drm_mode_create_dithering_property(struct drm_device *dev) -{ - struct drm_property *dithering_mode; - - if (dev->mode_config.dithering_mode_property) - return 0; - - dithering_mode = - drm_property_create_enum(dev, 0, "dithering", - drm_dithering_mode_enum_list, - ARRAY_SIZE(drm_dithering_mode_enum_list)); - dev->mode_config.dithering_mode_property = dithering_mode; - - return 0; -} -EXPORT_SYMBOL(drm_mode_create_dithering_property); - /** * drm_mode_create_dirty_property - create dirty property * @dev: DRM device diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index eb40a967fc65..781988f637ec 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -982,7 +982,6 @@ extern int drm_mode_create_dvi_i_properties(struct drm_device *dev); extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats, char *formats[]); extern int drm_mode_create_scaling_mode_property(struct drm_device *dev); -extern int drm_mode_create_dithering_property(struct drm_device *dev); extern int drm_mode_create_dirty_info_property(struct drm_device *dev); extern const char *drm_get_encoder_name(const struct drm_encoder *encoder); -- cgit v1.2.3-71-gd317 From 67587e8689ffbb788468c738f07a9678bde51084 Mon Sep 17 00:00:00 2001 From: "Lespiau, Damien" Date: Tue, 20 Aug 2013 00:53:05 +0100 Subject: drm: Remove drm_mode_list_concat() The last user was removed in commit 575dc34ee0de867ba83abf25998e0963bff451fa Author: Dave Airlie Date: Mon Sep 7 18:43:26 2009 +1000 drm/kms: remove old std mode fallback code. The new code adds modes in the helper, which makes more sense I disliked the non-driver code adding modes. Signed-off-by: Damien Lespiau Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_modes.c | 21 --------------------- include/drm/drm_crtc.h | 2 -- 2 files changed, 23 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 504a602f495c..fc2adb62b757 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -595,27 +595,6 @@ void drm_mode_set_name(struct drm_display_mode *mode) } EXPORT_SYMBOL(drm_mode_set_name); -/** - * drm_mode_list_concat - move modes from one list to another - * @head: source list - * @new: dst list - * - * LOCKING: - * Caller must ensure both lists are locked. - * - * Move all the modes from @head to @new. - */ -void drm_mode_list_concat(struct list_head *head, struct list_head *new) -{ - - struct list_head *entry, *tmp; - - list_for_each_safe(entry, tmp, head) { - list_move_tail(entry, new); - } -} -EXPORT_SYMBOL(drm_mode_list_concat); - /** * drm_mode_width - get the width of a mode * @mode: mode diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 781988f637ec..39d84dc7abcf 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -927,8 +927,6 @@ extern int drm_mode_height(const struct drm_display_mode *mode); /* for us by fb module */ extern struct drm_display_mode *drm_mode_create(struct drm_device *dev); extern void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode); -extern void drm_mode_list_concat(struct list_head *head, - struct list_head *new); extern void drm_mode_validate_size(struct drm_device *dev, struct list_head *mode_list, int maxX, int maxY, int maxPitch); -- cgit v1.2.3-71-gd317 From 86f422d5be001cfe311fc46d60a6e3ef6868dd40 Mon Sep 17 00:00:00 2001 From: "Lespiau, Damien" Date: Tue, 20 Aug 2013 00:53:06 +0100 Subject: drm: Make drm_mode_remove() static It's only used in drm_crtc.c. Signed-off-by: Damien Lespiau Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 7 +++---- include/drm/drm_crtc.h | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index ffb791f95d45..54b4169fc48e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -686,20 +686,19 @@ void drm_mode_probed_add(struct drm_connector *connector, } EXPORT_SYMBOL(drm_mode_probed_add); -/** +/* * drm_mode_remove - remove and free a mode * @connector: connector list to modify * @mode: mode to remove * * Remove @mode from @connector's mode list, then free it. */ -void drm_mode_remove(struct drm_connector *connector, - struct drm_display_mode *mode) +static void drm_mode_remove(struct drm_connector *connector, + struct drm_display_mode *mode) { list_del(&mode->head); drm_mode_destroy(connector->dev, mode); } -EXPORT_SYMBOL(drm_mode_remove); /** * drm_connector_init - Init a preallocated connector diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 39d84dc7abcf..014e491c3c1c 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -910,7 +910,6 @@ extern struct edid *drm_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter); extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid); extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode); -extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode); extern void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src); extern struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, const struct drm_display_mode *mode); -- cgit v1.2.3-71-gd317 From a03eb8388d91eb1c8dae79b790e6560134977a15 Mon Sep 17 00:00:00 2001 From: "Lespiau, Damien" Date: Tue, 20 Aug 2013 00:53:07 +0100 Subject: drm: Remove 2 unused defines These were introduced in the very first DRM commit: commit f453ba0460742ad027ae0c4c7d61e62817b3e7ef Author: Dave Airlie Date: Fri Nov 7 14:05:41 2008 -0800 DRM: add mode setting support Add mode setting support to the DRM layer. But are unused. Signed-off-by: Damien Lespiau Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- include/drm/drm_crtc.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 014e491c3c1c..0a9f73e8be26 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -494,8 +494,6 @@ struct drm_encoder_funcs { void (*destroy)(struct drm_encoder *encoder); }; -#define DRM_CONNECTOR_MAX_UMODES 16 -#define DRM_CONNECTOR_LEN 32 #define DRM_CONNECTOR_MAX_ENCODER 3 /** -- cgit v1.2.3-71-gd317 From 2c9c52e85318c67cfc70f4885ad543abf0f845e6 Mon Sep 17 00:00:00 2001 From: "Lespiau, Damien" Date: Tue, 20 Aug 2013 00:53:08 +0100 Subject: drm: Make drm_fb_cma_describe() static This function is only used in drm_fb_cma_helper.c. Signed-off-by: Damien Lespiau Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_fb_cma_helper.c | 5 ++--- include/drm/drm_fb_cma_helper.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index c385cc5e730e..61b5a47ad239 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -181,11 +181,11 @@ struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj); #ifdef CONFIG_DEBUG_FS -/** +/* * drm_fb_cma_describe() - Helper to dump information about a single * CMA framebuffer object */ -void drm_fb_cma_describe(struct drm_framebuffer *fb, struct seq_file *m) +static void drm_fb_cma_describe(struct drm_framebuffer *fb, struct seq_file *m) { struct drm_fb_cma *fb_cma = to_fb_cma(fb); int i, n = drm_format_num_planes(fb->pixel_format); @@ -199,7 +199,6 @@ void drm_fb_cma_describe(struct drm_framebuffer *fb, struct seq_file *m) drm_gem_cma_describe(fb_cma->obj[i], m); } } -EXPORT_SYMBOL_GPL(drm_fb_cma_describe); /** * drm_fb_cma_debugfs_show() - Helper to list CMA framebuffer objects diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h index 4a3fc244301c..c54cf3d4a03f 100644 --- a/include/drm/drm_fb_cma_helper.h +++ b/include/drm/drm_fb_cma_helper.h @@ -24,7 +24,6 @@ struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, unsigned int plane); #ifdef CONFIG_DEBUG_FS -void drm_fb_cma_describe(struct drm_framebuffer *fb, struct seq_file *m); int drm_fb_cma_debugfs_show(struct seq_file *m, void *arg); #endif -- cgit v1.2.3-71-gd317 From 15f3b9d95bf2c85afcf11e7536c17eaeedfcefe7 Mon Sep 17 00:00:00 2001 From: "Lespiau, Damien" Date: Tue, 20 Aug 2013 00:53:09 +0100 Subject: drm: Remove unused PCI ids Signed-off-by: Damien Lespiau Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- include/drm/drm_pciids.h | 30 ------------------------------ 1 file changed, 30 deletions(-) (limited to 'include') diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index 34efaf64cc87..0a85e5c5d61b 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -690,29 +690,6 @@ {0x102b, 0x2527, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MGA_CARD_TYPE_G550}, \ {0, 0, 0} -#define mach64_PCI_IDS \ - {0x1002, 0x4749, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4750, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4751, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4742, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4744, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4c49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4c50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4c51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4c42, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4c44, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x474c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x474f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4752, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4753, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x474d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x474e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4c52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4c53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4c4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0x1002, 0x4c4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0, 0, 0} - #define sisdrv_PCI_IDS \ {0x1039, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0x1039, 0x5300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ @@ -752,10 +729,6 @@ {0x8086, 0x1132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ {0, 0, 0} -#define gamma_PCI_IDS \ - {0x3d3d, 0x0008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \ - {0, 0, 0} - #define savage_PCI_IDS \ {0x5333, 0x8a20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SAVAGE3D}, \ {0x5333, 0x8a21, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_SAVAGE3D}, \ @@ -781,6 +754,3 @@ {0x5333, 0x8d03, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_PROSAVAGEDDR}, \ {0x5333, 0x8d04, PCI_ANY_ID, PCI_ANY_ID, 0, 0, S3_PROSAVAGEDDR}, \ {0, 0, 0} - -#define ffb_PCI_IDS \ - {0, 0, 0} -- cgit v1.2.3-71-gd317 From 66cc8b6b8b2b3b5c9a67429af04ec356ff7fcfa4 Mon Sep 17 00:00:00 2001 From: "Lespiau, Damien" Date: Tue, 20 Aug 2013 00:53:10 +0100 Subject: drm: Make drm_get_platform_dev() static It's only used in drm_platform.c. Signed-off-by: Damien Lespiau Reviewed-by: Alex Deucher Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_platform.c | 7 +++---- include/drm/drmP.h | 3 --- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index b8a282ea8751..400024b6d512 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -28,7 +28,7 @@ #include #include -/** +/* * Register. * * \param platdev - Platform device struture @@ -39,8 +39,8 @@ * Try and register, if we fail to register, backout previous work. */ -int drm_get_platform_dev(struct platform_device *platdev, - struct drm_driver *driver) +static int drm_get_platform_dev(struct platform_device *platdev, + struct drm_driver *driver) { struct drm_device *dev; int ret; @@ -107,7 +107,6 @@ err_g1: mutex_unlock(&drm_global_mutex); return ret; } -EXPORT_SYMBOL(drm_get_platform_dev); static int drm_platform_get_irq(struct drm_device *dev) { diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 046a7db43a6c..1a7a78fdb4b7 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1657,9 +1657,6 @@ extern int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *speed_mask); extern int drm_platform_init(struct drm_driver *driver, struct platform_device *platform_device); extern void drm_platform_exit(struct drm_driver *driver, struct platform_device *platform_device); -extern int drm_get_platform_dev(struct platform_device *pdev, - struct drm_driver *driver); - /* returns true if currently okay to sleep */ static __inline__ bool drm_can_sleep(void) { -- cgit v1.2.3-71-gd317 From a8e11d1c435f9d185c9f3b1981b9613a579b9999 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:37 +0200 Subject: drm/gem: fix up flink name create race This is the 2nd attempt, I've always been a bit dissatisified with the tricky nature of the first one: http://lists.freedesktop.org/archives/dri-devel/2012-July/025451.html The issue is that the flink ioctl can race with calling gem_close on the last gem handle. In that case we'll end up with a zero handle count, but an flink name (and it's corresponding reference). Which results in a neat space leak. In my first attempt I've solved this by rechecking the handle count. But fundamentally the issue is that ->handle_count isn't your usual refcount - it can be resurrected from 0 among other things. For those special beasts atomic_t often suggest way more ordering that it actually guarantees. To prevent being tricked by those hairy semantics take the easy way out and simply protect the handle with the existing dev->object_name_lock. With that change implemented it's dead easy to fix the flink vs. gem close reace: When we try to create the name we simply have to check whether there's still officially a gem handle around and if not refuse to create the flink name. Since the handle count decrement and flink name destruction is now also protected by that lock the reace is gone and we can't ever leak the flink reference again. Outside of the drm core only the exynos driver looks at the handle count, and tbh I have no idea why (it's just for debug dmesg output luckily). I've considered inlining the drm_gem_object_handle_free, but I plan to add more name-like things (like the exported dma_buf) to this scheme, so it's clearer to leave the handle freeing in its own function. This is exercised by the new gem_flink_race i-g-t testcase, which on my snb leaks gem objects at a rate of roughly 1k objects/s. v2: Fix up the error path handling in handle_create and make it more robust by simply calling object_handle_unreference. v3: Fix up the handle_unreference logic bug - atomic_dec_and_test retursn 1 for 0. Oops. v4: Squash in inlining of drm_gem_object_handle_reference as suggested by Dave Airlie and add a note that we now have a testcase. Cc: Dave Airlie Cc: Inki Dae Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 31 ++++++++++++++++++++----------- drivers/gpu/drm/drm_info.c | 2 +- drivers/gpu/drm/exynos/exynos_drm_gem.c | 2 +- include/drm/drmP.h | 19 ++++++++++--------- 4 files changed, 32 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index dcbd2f559e39..b8a8132becef 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -154,7 +154,7 @@ void drm_gem_private_object_init(struct drm_device *dev, obj->filp = NULL; kref_init(&obj->refcount); - atomic_set(&obj->handle_count, 0); + obj->handle_count = 0; obj->size = size; } EXPORT_SYMBOL(drm_gem_private_object_init); @@ -218,11 +218,9 @@ static void drm_gem_object_handle_free(struct drm_gem_object *obj) struct drm_device *dev = obj->dev; /* Remove any name for this object */ - spin_lock(&dev->object_name_lock); if (obj->name) { idr_remove(&dev->object_name_idr, obj->name); obj->name = 0; - spin_unlock(&dev->object_name_lock); /* * The object name held a reference to this object, drop * that now. @@ -230,15 +228,13 @@ static void drm_gem_object_handle_free(struct drm_gem_object *obj) * This cannot be the last reference, since the handle holds one too. */ kref_put(&obj->refcount, drm_gem_object_ref_bug); - } else - spin_unlock(&dev->object_name_lock); - + } } void drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) { - if (WARN_ON(atomic_read(&obj->handle_count) == 0)) + if (WARN_ON(obj->handle_count == 0)) return; /* @@ -247,8 +243,11 @@ drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) * checked for a name */ - if (atomic_dec_and_test(&obj->handle_count)) + spin_lock(&obj->dev->object_name_lock); + if (--obj->handle_count == 0) drm_gem_object_handle_free(obj); + spin_unlock(&obj->dev->object_name_lock); + drm_gem_object_unreference_unlocked(obj); } @@ -326,17 +325,21 @@ drm_gem_handle_create(struct drm_file *file_priv, * allocation under our spinlock. */ idr_preload(GFP_KERNEL); + spin_lock(&dev->object_name_lock); spin_lock(&file_priv->table_lock); ret = idr_alloc(&file_priv->object_idr, obj, 1, 0, GFP_NOWAIT); - + drm_gem_object_reference(obj); + obj->handle_count++; spin_unlock(&file_priv->table_lock); + spin_unlock(&dev->object_name_lock); idr_preload_end(); - if (ret < 0) + if (ret < 0) { + drm_gem_object_handle_unreference_unlocked(obj); return ret; + } *handlep = ret; - drm_gem_object_handle_reference(obj); if (dev->driver->gem_open_object) { ret = dev->driver->gem_open_object(obj, file_priv); @@ -577,6 +580,12 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data, idr_preload(GFP_KERNEL); spin_lock(&dev->object_name_lock); + /* prevent races with concurrent gem_close. */ + if (obj->handle_count == 0) { + ret = -ENOENT; + goto err; + } + if (!obj->name) { ret = idr_alloc(&dev->object_name_idr, obj, 1, 0, GFP_NOWAIT); if (ret < 0) diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c index 9f8fc4c328c9..5351e811c421 100644 --- a/drivers/gpu/drm/drm_info.c +++ b/drivers/gpu/drm/drm_info.c @@ -207,7 +207,7 @@ static int drm_gem_one_name_info(int id, void *ptr, void *data) seq_printf(m, "%6d %8zd %7d %8d\n", obj->name, obj->size, - atomic_read(&obj->handle_count), + obj->handle_count, atomic_read(&obj->refcount.refcount)); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index b904633863e8..f3c6f40666e1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -136,7 +136,7 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) obj = &exynos_gem_obj->base; buf = exynos_gem_obj->buffer; - DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count)); + DRM_DEBUG_KMS("handle count = %d\n", obj->handle_count); /* * do not release memory region from exporter. diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 1a7a78fdb4b7..57dce6081d73 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -615,8 +615,16 @@ struct drm_gem_object { /** Reference count of this object */ struct kref refcount; - /** Handle count of this object. Each handle also holds a reference */ - atomic_t handle_count; /* number of handles on this object */ + /** + * handle_count - gem file_priv handle count of this object + * + * Each handle also holds a reference. Note that when the handle_count + * drops to 0 any global names (e.g. the id in the flink namespace) will + * be cleared. + * + * Protected by dev->object_name_lock. + * */ + unsigned handle_count; /** Related drm device */ struct drm_device *dev; @@ -1572,13 +1580,6 @@ int drm_gem_handle_create(struct drm_file *file_priv, u32 *handlep); int drm_gem_handle_delete(struct drm_file *filp, u32 handle); -static inline void -drm_gem_object_handle_reference(struct drm_gem_object *obj) -{ - drm_gem_object_reference(obj); - atomic_inc(&obj->handle_count); -} - void drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj); void drm_gem_free_mmap_offset(struct drm_gem_object *obj); -- cgit v1.2.3-71-gd317 From becee2a57fd2b64c53ebef58277fbca895cf8ec1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:39 +0200 Subject: drm/gem: make drm_gem_object_handle_unreference_unlocked static No one outside of drm should use this, the official interfaces are drm_gem_handle_create and drm_gem_handle_delete. The handle refcounting is purely an implementation detail of gem. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 2 +- include/drm/drmP.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index b8a8132becef..52548fd34b15 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -231,7 +231,7 @@ static void drm_gem_object_handle_free(struct drm_gem_object *obj) } } -void +static void drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) { if (WARN_ON(obj->handle_count == 0)) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 57dce6081d73..7782dbbbe126 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1580,7 +1580,6 @@ int drm_gem_handle_create(struct drm_file *file_priv, u32 *handlep); int drm_gem_handle_delete(struct drm_file *filp, u32 handle); -void drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj); void drm_gem_free_mmap_offset(struct drm_gem_object *obj); int drm_gem_create_mmap_offset(struct drm_gem_object *obj); -- cgit v1.2.3-71-gd317 From cd4f013f3a4b6a55d484cc2e206dc08e055e5291 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:44 +0200 Subject: drm/gem: switch dev->object_name_lock to a mutex I want to wrap the creation of a dma-buf from a gem object in it, so that the obj->export_dma_buf cache can be atomically filled in. Instead of creating a new mutex just for that variable I've figured I can reuse the existing dev->object_name_lock, especially since the new semantics will exactly mirror the flink obj->name already protected by that lock. v2: idr_preload/idr_preload_end is now an atomic section, so need to move the mutex locking outside. [airlied: fix up conflict with patch to make debugfs use lock] Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 18 +++++++++--------- drivers/gpu/drm/drm_info.c | 4 ++-- include/drm/drmP.h | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 52548fd34b15..adb9eda4fa1a 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -93,7 +93,7 @@ drm_gem_init(struct drm_device *dev) { struct drm_gem_mm *mm; - spin_lock_init(&dev->object_name_lock); + mutex_init(&dev->object_name_lock); idr_init(&dev->object_name_idr); mm = kzalloc(sizeof(struct drm_gem_mm), GFP_KERNEL); @@ -243,10 +243,10 @@ drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) * checked for a name */ - spin_lock(&obj->dev->object_name_lock); + mutex_lock(&obj->dev->object_name_lock); if (--obj->handle_count == 0) drm_gem_object_handle_free(obj); - spin_unlock(&obj->dev->object_name_lock); + mutex_unlock(&obj->dev->object_name_lock); drm_gem_object_unreference_unlocked(obj); } @@ -324,16 +324,16 @@ drm_gem_handle_create(struct drm_file *file_priv, * Get the user-visible handle using idr. Preload and perform * allocation under our spinlock. */ + mutex_lock(&dev->object_name_lock); idr_preload(GFP_KERNEL); - spin_lock(&dev->object_name_lock); spin_lock(&file_priv->table_lock); ret = idr_alloc(&file_priv->object_idr, obj, 1, 0, GFP_NOWAIT); drm_gem_object_reference(obj); obj->handle_count++; spin_unlock(&file_priv->table_lock); - spin_unlock(&dev->object_name_lock); idr_preload_end(); + mutex_unlock(&dev->object_name_lock); if (ret < 0) { drm_gem_object_handle_unreference_unlocked(obj); return ret; @@ -578,8 +578,8 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data, if (obj == NULL) return -ENOENT; + mutex_lock(&dev->object_name_lock); idr_preload(GFP_KERNEL); - spin_lock(&dev->object_name_lock); /* prevent races with concurrent gem_close. */ if (obj->handle_count == 0) { ret = -ENOENT; @@ -601,8 +601,8 @@ drm_gem_flink_ioctl(struct drm_device *dev, void *data, ret = 0; err: - spin_unlock(&dev->object_name_lock); idr_preload_end(); + mutex_unlock(&dev->object_name_lock); drm_gem_object_unreference_unlocked(obj); return ret; } @@ -625,11 +625,11 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data, if (!(dev->driver->driver_features & DRIVER_GEM)) return -ENODEV; - spin_lock(&dev->object_name_lock); + mutex_lock(&dev->object_name_lock); obj = idr_find(&dev->object_name_idr, (int) args->name); if (obj) drm_gem_object_reference(obj); - spin_unlock(&dev->object_name_lock); + mutex_unlock(&dev->object_name_lock); if (!obj) return -ENOENT; diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c index 5351e811c421..53298320080b 100644 --- a/drivers/gpu/drm/drm_info.c +++ b/drivers/gpu/drm/drm_info.c @@ -219,9 +219,9 @@ int drm_gem_name_info(struct seq_file *m, void *data) seq_printf(m, " name size handles refcount\n"); - spin_lock(&dev->object_name_lock); + mutex_lock(&dev->object_name_lock); idr_for_each(&dev->object_name_idr, drm_gem_one_name_info, m); - spin_unlock(&dev->object_name_lock); + mutex_unlock(&dev->object_name_lock); return 0; } diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 7782dbbbe126..bf058470a0fd 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1196,7 +1196,7 @@ struct drm_device { /** \name GEM information */ /*@{ */ - spinlock_t object_name_lock; + struct mutex object_name_lock; struct idr object_name_idr; /*@} */ int switch_power_state; -- cgit v1.2.3-71-gd317 From 20228c447846da9399ead53fdbbc8ab69b47788a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:45 +0200 Subject: drm/gem: completely close gem_open vs. gem_close races The gem flink name holds a reference onto the object itself, and this self-reference would prevent an flink'ed object from every being freed. To break that loop we remove the flink name when the last userspace handle disappears, i.e. when obj->handle_count reaches 0. Now in gem_open we drop the dev->object_name_lock between the flink name lookup and actually adding the handle. This means a concurrent gem_close of the last handle could result in the flink name getting reaped right inbetween, i.e. Thread 1 Thread 2 gem_open gem_close flink -> obj lookup handle_count drops to 0 remove flink name create_handle handle_count++ If someone now flinks this object again, we'll get a new flink name. We can close this race by removing the lock dropping and making the entire lookup+handle_create sequence atomic. Unfortunately to still be able to share the handle_create logic this requires a handle_create_tail function which drops the lock - we can't hold the object_name_lock while calling into a driver's ->gem_open callback. Note that for flink fixing this race isn't really important, since racing gem_open against gem_close is clearly a userspace bug. And no matter how the race ends, we won't leak any references. But with dma-buf where the userspace dma-buf fd itself is refcounted this is a valid sequence and hence we should fix it. Therefore this patch here is just a warm-up exercise (and for consistency between flink buffer sharing and dma-buf buffer sharing with self-imports). Also note that this extension of the critical section in gem_open protected by dev->object_name_lock only works because it's now a mutex: A spinlock would conflict with the potential memory allocation in idr_preload(). This is exercises by igt/gem_flink_race/flink_name. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 42 +++++++++++++++++++++++++++++++----------- include/drm/drmP.h | 3 +++ 2 files changed, 34 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index adb9eda4fa1a..d47aa774d64b 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -308,23 +308,26 @@ int drm_gem_dumb_destroy(struct drm_file *file, EXPORT_SYMBOL(drm_gem_dumb_destroy); /** - * Create a handle for this object. This adds a handle reference - * to the object, which includes a regular reference count. Callers - * will likely want to dereference the object afterwards. + * drm_gem_handle_create_tail - internal functions to create a handle + * + * This expects the dev->object_name_lock to be held already and will drop it + * before returning. Used to avoid races in establishing new handles when + * importing an object from either an flink name or a dma-buf. */ int -drm_gem_handle_create(struct drm_file *file_priv, - struct drm_gem_object *obj, - u32 *handlep) +drm_gem_handle_create_tail(struct drm_file *file_priv, + struct drm_gem_object *obj, + u32 *handlep) { struct drm_device *dev = obj->dev; int ret; + WARN_ON(!mutex_is_locked(&dev->object_name_lock)); + /* * Get the user-visible handle using idr. Preload and perform * allocation under our spinlock. */ - mutex_lock(&dev->object_name_lock); idr_preload(GFP_KERNEL); spin_lock(&file_priv->table_lock); @@ -351,6 +354,21 @@ drm_gem_handle_create(struct drm_file *file_priv, return 0; } + +/** + * Create a handle for this object. This adds a handle reference + * to the object, which includes a regular reference count. Callers + * will likely want to dereference the object afterwards. + */ +int +drm_gem_handle_create(struct drm_file *file_priv, + struct drm_gem_object *obj, + u32 *handlep) +{ + mutex_lock(&obj->dev->object_name_lock); + + return drm_gem_handle_create_tail(file_priv, obj, handlep); +} EXPORT_SYMBOL(drm_gem_handle_create); @@ -627,13 +645,15 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data, mutex_lock(&dev->object_name_lock); obj = idr_find(&dev->object_name_idr, (int) args->name); - if (obj) + if (obj) { drm_gem_object_reference(obj); - mutex_unlock(&dev->object_name_lock); - if (!obj) + } else { + mutex_unlock(&dev->object_name_lock); return -ENOENT; + } - ret = drm_gem_handle_create(file_priv, obj, &handle); + /* drm_gem_handle_create_tail unlocks dev->object_name_lock. */ + ret = drm_gem_handle_create_tail(file_priv, obj, &handle); drm_gem_object_unreference_unlocked(obj); if (ret) return ret; diff --git a/include/drm/drmP.h b/include/drm/drmP.h index bf058470a0fd..063eac31b97b 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1575,6 +1575,9 @@ drm_gem_object_unreference_unlocked(struct drm_gem_object *obj) } } +int drm_gem_handle_create_tail(struct drm_file *file_priv, + struct drm_gem_object *obj, + u32 *handlep); int drm_gem_handle_create(struct drm_file *file_priv, struct drm_gem_object *obj, u32 *handlep); -- cgit v1.2.3-71-gd317 From 319c933c71f3dbdb2b3274d1634d3494c70efa06 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:46 +0200 Subject: drm/prime: proper locking+refcounting for obj->dma_buf link The export dma-buf cache is semantically similar to an flink name. So semantically it makes sense to treat it the same and remove the name (i.e. the dma_buf pointer) and its references when the last gem handle disappears. Again we need to be careful, but double so: Not just could someone race and export with a gem close ioctl (so we need to recheck obj->handle_count again when assigning the new name), but multiple exports can also race against each another. This is prevented by holding the dev->object_name_lock across the entire section which touches obj->dma_buf. With the new scheme we also need to reinstate the obj->dma_buf link at import time (in case the only reference userspace has held in-between was through the dma-buf fd and not through any native gem handle). For simplicity we don't check whether it's a native object but unconditionally set up that link - with the new scheme of removing the obj->dma_buf reference when the last handle disappears we can do that. To make it clear that this is not just for exported buffers anymore als rename it from export_dma_buf to dma_buf. To make sure that now one can race a fd_to_handle or handle_to_fd with gem_close we use the same tricks as in flink of extending the dev->object_name_locking critical section. With this change we finally have a guaranteed 1:1 relationship (at least for native objects) between gem objects and dma-bufs, even accounting for races (which can happen since the dma-buf itself holds a reference while in-flight). This prevent igt/prime_self_import/export-vs-gem_close-race from Oopsing the kernel. There is still a leak though since the per-file priv dma-buf/handle cache handling is racy. That will be fixed in a later patch. v2: Remove the bogus dma_buf_put from the export_and_register_object failure path if we've raced with the handle count dropping to 0. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_fops.c | 1 + drivers/gpu/drm/drm_gem.c | 24 ++++++++++++++-- drivers/gpu/drm/drm_prime.c | 70 +++++++++++++++++++++++++++++++++++---------- include/drm/drmP.h | 12 ++++++-- 4 files changed, 87 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 59f459291093..2d2401e9c5ae 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -486,6 +486,7 @@ int drm_release(struct inode *inode, struct file *filp) if (dev->driver->postclose) dev->driver->postclose(dev, file_priv); + if (drm_core_check_feature(dev, DRIVER_PRIME)) drm_prime_destroy_file_private(&file_priv->prime); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index d47aa774d64b..4b3c533be859 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -195,9 +195,14 @@ drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) drm_prime_remove_buf_handle(&filp->prime, obj->import_attach->dmabuf); } - if (obj->export_dma_buf) { + + /* + * Note: obj->dma_buf can't disappear as long as we still hold a + * handle reference in obj->handle_count. + */ + if (obj->dma_buf) { drm_prime_remove_buf_handle(&filp->prime, - obj->export_dma_buf); + obj->dma_buf); } } @@ -231,6 +236,15 @@ static void drm_gem_object_handle_free(struct drm_gem_object *obj) } } +static void drm_gem_object_exported_dma_buf_free(struct drm_gem_object *obj) +{ + /* Unbreak the reference cycle if we have an exported dma_buf. */ + if (obj->dma_buf) { + dma_buf_put(obj->dma_buf); + obj->dma_buf = NULL; + } +} + static void drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) { @@ -244,8 +258,10 @@ drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) */ mutex_lock(&obj->dev->object_name_lock); - if (--obj->handle_count == 0) + if (--obj->handle_count == 0) { drm_gem_object_handle_free(obj); + drm_gem_object_exported_dma_buf_free(obj); + } mutex_unlock(&obj->dev->object_name_lock); drm_gem_object_unreference_unlocked(obj); @@ -712,6 +728,8 @@ drm_gem_release(struct drm_device *dev, struct drm_file *file_private) void drm_gem_object_release(struct drm_gem_object *obj) { + WARN_ON(obj->dma_buf); + if (obj->filp) fput(obj->filp); } diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 3d576018893a..5e543e9264d7 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -193,11 +193,8 @@ void drm_gem_dmabuf_release(struct dma_buf *dma_buf) { struct drm_gem_object *obj = dma_buf->priv; - if (obj->export_dma_buf == dma_buf) { - /* drop the reference on the export fd holds */ - obj->export_dma_buf = NULL; - drm_gem_object_unreference_unlocked(obj); - } + /* drop the reference on the export fd holds */ + drm_gem_object_unreference_unlocked(obj); } EXPORT_SYMBOL(drm_gem_dmabuf_release); @@ -298,6 +295,37 @@ struct dma_buf *drm_gem_prime_export(struct drm_device *dev, } EXPORT_SYMBOL(drm_gem_prime_export); +static struct dma_buf *export_and_register_object(struct drm_device *dev, + struct drm_gem_object *obj, + uint32_t flags) +{ + struct dma_buf *dmabuf; + + /* prevent races with concurrent gem_close. */ + if (obj->handle_count == 0) { + dmabuf = ERR_PTR(-ENOENT); + return dmabuf; + } + + dmabuf = dev->driver->gem_prime_export(dev, obj, flags); + if (IS_ERR(dmabuf)) { + /* normally the created dma-buf takes ownership of the ref, + * but if that fails then drop the ref + */ + return dmabuf; + } + + /* + * Note that callers do not need to clean up the export cache + * since the check for obj->handle_count guarantees that someone + * will clean it up. + */ + obj->dma_buf = dmabuf; + get_dma_buf(obj->dma_buf); + + return dmabuf; +} + int drm_gem_prime_handle_to_fd(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags, int *prime_fd) @@ -313,15 +341,20 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, /* re-export the original imported object */ if (obj->import_attach) { dmabuf = obj->import_attach->dmabuf; + get_dma_buf(dmabuf); goto out_have_obj; } - if (obj->export_dma_buf) { - dmabuf = obj->export_dma_buf; + mutex_lock(&dev->object_name_lock); + if (obj->dma_buf) { + get_dma_buf(obj->dma_buf); + dmabuf = obj->dma_buf; + mutex_unlock(&dev->object_name_lock); goto out_have_obj; } - dmabuf = dev->driver->gem_prime_export(dev, obj, flags); + dmabuf = export_and_register_object(dev, obj, flags); + mutex_unlock(&dev->object_name_lock); if (IS_ERR(dmabuf)) { /* normally the created dma-buf takes ownership of the ref, * but if that fails then drop the ref @@ -329,14 +362,13 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, ret = PTR_ERR(dmabuf); goto out; } - obj->export_dma_buf = dmabuf; mutex_lock(&file_priv->prime.lock); /* if we've exported this buffer the cheat and add it to the import list * so we get the correct handle back */ ret = drm_prime_add_buf_handle(&file_priv->prime, - obj->export_dma_buf, handle); + dmabuf, handle); if (ret) goto fail_put_dmabuf; @@ -349,7 +381,6 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, return 0; out_have_obj: - get_dma_buf(dmabuf); ret = dma_buf_fd(dmabuf, flags); if (ret < 0) { dma_buf_put(dmabuf); @@ -365,8 +396,6 @@ fail_rm_handle: dmabuf); mutex_unlock(&file_priv->prime.lock); fail_put_dmabuf: - /* clear NOT to be checked when releasing dma_buf */ - obj->export_dma_buf = NULL; dma_buf_put(dmabuf); out: drm_gem_object_unreference_unlocked(obj); @@ -448,13 +477,22 @@ int drm_gem_prime_fd_to_handle(struct drm_device *dev, goto out_put; /* never seen this one, need to import */ + mutex_lock(&dev->object_name_lock); obj = dev->driver->gem_prime_import(dev, dma_buf); if (IS_ERR(obj)) { ret = PTR_ERR(obj); - goto out_put; + goto out_unlock; + } + + if (obj->dma_buf) { + WARN_ON(obj->dma_buf != dma_buf); + } else { + obj->dma_buf = dma_buf; + get_dma_buf(dma_buf); } - ret = drm_gem_handle_create(file_priv, obj, handle); + /* drm_gem_handle_create_tail unlocks dev->object_name_lock. */ + ret = drm_gem_handle_create_tail(file_priv, obj, handle); drm_gem_object_unreference_unlocked(obj); if (ret) goto out_put; @@ -475,6 +513,8 @@ fail: * to detach.. which seems ok.. */ drm_gem_handle_delete(file_priv, *handle); +out_unlock: + mutex_lock(&dev->object_name_lock); out_put: dma_buf_put(dma_buf); mutex_unlock(&file_priv->prime.lock); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 063eac31b97b..a95db49b3f9e 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -667,8 +667,16 @@ struct drm_gem_object { void *driver_private; - /* dma buf exported from this GEM object */ - struct dma_buf *export_dma_buf; + /** + * dma_buf - dma buf associated with this GEM object + * + * Pointer to the dma-buf associated with this gem object (either + * through importing or exporting). We break the resulting reference + * loop when the last gem handle for this object is released. + * + * Protected by obj->object_name_lock + */ + struct dma_buf *dma_buf; /** * import_attach - dma buf attachment backing this object -- cgit v1.2.3-71-gd317 From 838cd4455ee1c76db06175d44319a8e7ac114b0e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:47 +0200 Subject: drm/prime: Simplify drm_gem_remove_prime_handles with the reworking semantics and locking of the obj->dma_buf pointer this pointer is always set as long as there's still a gem handle around and a dma_buf associated with this gem object. Also, the per file-priv lookup-cache for dma-buf importing is also unified between foreign and native objects. Hence we don't need to special case the clean any more and can simply drop the clause which only runs for foreing objects, i.e. with obj->import_attach set. Note that with this change (actually with the previous one to always set up obj->dma_buf even for foreign objects) it is no longer required to set obj->import_attach when importing a foreing object. So update comments accordingly, too. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 5 ----- include/drm/drmP.h | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 4b3c533be859..0a5a0ca0a52e 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -191,11 +191,6 @@ EXPORT_SYMBOL(drm_gem_object_alloc); static void drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) { - if (obj->import_attach) { - drm_prime_remove_buf_handle(&filp->prime, - obj->import_attach->dmabuf); - } - /* * Note: obj->dma_buf can't disappear as long as we still hold a * handle reference in obj->handle_count. diff --git a/include/drm/drmP.h b/include/drm/drmP.h index a95db49b3f9e..ce1e6bd30306 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -687,6 +687,11 @@ struct drm_gem_object { * * The driver's ->gem_free_object callback is responsible for cleaning * up the dma_buf attachment and references acquired at import time. + * + * Note that the drm gem/prime core does not depend upon drivers setting + * this field any more. So for drivers where this doesn't make sense + * (e.g. virtual devices or a displaylink behind an usb bus) they can + * simply leave it as NULL. */ struct dma_buf_attachment *import_attach; }; -- cgit v1.2.3-71-gd317 From de9564d8b9e69bf6603521e810d3cb46fa98ad81 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:48 +0200 Subject: drm/prime: make drm_prime_lookup_buf_handle static ... and move it to the top of the function to avoid a forward declaration. Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_prime.c | 29 +++++++++++++++-------------- include/drm/drmP.h | 1 - 2 files changed, 15 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 5e543e9264d7..ed1ea5c1a9ca 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -83,6 +83,21 @@ static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, return 0; } +static int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpriv, + struct dma_buf *dma_buf, + uint32_t *handle) +{ + struct drm_prime_member *member; + + list_for_each_entry(member, &prime_fpriv->head, entry) { + if (member->dma_buf == dma_buf) { + *handle = member->handle; + return 0; + } + } + return -ENOENT; +} + static int drm_gem_map_attach(struct dma_buf *dma_buf, struct device *target_dev, struct dma_buf_attachment *attach) @@ -655,20 +670,6 @@ void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv) } EXPORT_SYMBOL(drm_prime_destroy_file_private); -int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle) -{ - struct drm_prime_member *member; - - list_for_each_entry(member, &prime_fpriv->head, entry) { - if (member->dma_buf == dma_buf) { - *handle = member->handle; - return 0; - } - } - return -ENOENT; -} -EXPORT_SYMBOL(drm_prime_lookup_buf_handle); - void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf) { mutex_lock(&prime_fpriv->lock); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index ce1e6bd30306..5914cc5c3fa6 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1508,7 +1508,6 @@ int drm_gem_dumb_destroy(struct drm_file *file, void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv); void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); -int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle); void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf); #if DRM_DEBUG_CODE -- cgit v1.2.3-71-gd317 From d0b2c5334f41bdd18adaa3fbc1f7b5f1daab7eac Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 15 Aug 2013 00:02:49 +0200 Subject: drm/prime: Always add exported buffers to the handle cache ... not only when the dma-buf is freshly created. In contrived examples someone else could have exported/imported the dma-buf already and handed us the gem object with a flink name. If such on object gets reexported as a dma_buf we won't have it in the handle cache already, which breaks the guarantee that for dma-buf imports we always hand back an existing handle if there is one. This is exercised by igt/prime_self_import/with_one_bo_two_files Now if we extend the locked sections just a notch more we can also plug th racy buf/handle cache setup in handle_to_fd: If evil userspace races a concurrent gem close against a prime export operation we can end up tearing down the gem handle before the dma buf handle cache is set up. When handle_to_fd gets around to adding the handle to the cache there will be no one left to clean it up, effectily leaking the bo (and the dma-buf, since the handle cache holds a ref on the dma-buf): Thread A Thread B handle_to_fd: lookup gem object from handle creates new dma_buf gem_close on the same handle obj->dma_buf is set, but file priv buf handle cache has no entry obj->handle_count drops to 0 drm_prime_add_buf_handle sets up the handle cache -> We have a dma-buf reference in the handle cache, but since the handle_count of the gem object already dropped to 0 no on will clean it up. When closing the drm device fd we'll hit the WARN_ON in drm_prime_destroy_file_private. The important change is to extend the critical section of the filp->prime.lock to cover the gem handle lookup. This serializes with a concurrent gem handle close. This leak is exercised by igt/prime_self_import/export-vs-gem_close-race Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 6 ++-- drivers/gpu/drm/drm_prime.c | 81 +++++++++++++++++++++++++++------------------ include/drm/drmP.h | 2 +- 3 files changed, 53 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 0a5a0ca0a52e..1ce88c3301a1 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -195,10 +195,12 @@ drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) * Note: obj->dma_buf can't disappear as long as we still hold a * handle reference in obj->handle_count. */ + mutex_lock(&filp->prime.lock); if (obj->dma_buf) { - drm_prime_remove_buf_handle(&filp->prime, - obj->dma_buf); + drm_prime_remove_buf_handle_locked(&filp->prime, + obj->dma_buf); } + mutex_unlock(&filp->prime.lock); } static void drm_gem_object_ref_bug(struct kref *list_kref) diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index ed1ea5c1a9ca..7ae2bfcab70e 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -83,6 +83,19 @@ static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, return 0; } +static struct dma_buf *drm_prime_lookup_buf_by_handle(struct drm_prime_file_private *prime_fpriv, + uint32_t handle) +{ + struct drm_prime_member *member; + + list_for_each_entry(member, &prime_fpriv->head, entry) { + if (member->handle == handle) + return member->dma_buf; + } + + return NULL; +} + static int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle) @@ -146,9 +159,8 @@ static void drm_gem_map_detach(struct dma_buf *dma_buf, attach->priv = NULL; } -static void drm_prime_remove_buf_handle_locked( - struct drm_prime_file_private *prime_fpriv, - struct dma_buf *dma_buf) +void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpriv, + struct dma_buf *dma_buf) { struct drm_prime_member *member, *safe; @@ -337,6 +349,8 @@ static struct dma_buf *export_and_register_object(struct drm_device *dev, */ obj->dma_buf = dmabuf; get_dma_buf(obj->dma_buf); + /* Grab a new ref since the callers is now used by the dma-buf */ + drm_gem_object_reference(obj); return dmabuf; } @@ -349,10 +363,20 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, int ret = 0; struct dma_buf *dmabuf; + mutex_lock(&file_priv->prime.lock); obj = drm_gem_object_lookup(dev, file_priv, handle); - if (!obj) - return -ENOENT; + if (!obj) { + ret = -ENOENT; + goto out_unlock; + } + + dmabuf = drm_prime_lookup_buf_by_handle(&file_priv->prime, handle); + if (dmabuf) { + get_dma_buf(dmabuf); + goto out_have_handle; + } + mutex_lock(&dev->object_name_lock); /* re-export the original imported object */ if (obj->import_attach) { dmabuf = obj->import_attach->dmabuf; @@ -360,45 +384,45 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, goto out_have_obj; } - mutex_lock(&dev->object_name_lock); if (obj->dma_buf) { get_dma_buf(obj->dma_buf); dmabuf = obj->dma_buf; - mutex_unlock(&dev->object_name_lock); goto out_have_obj; } dmabuf = export_and_register_object(dev, obj, flags); - mutex_unlock(&dev->object_name_lock); if (IS_ERR(dmabuf)) { /* normally the created dma-buf takes ownership of the ref, * but if that fails then drop the ref */ ret = PTR_ERR(dmabuf); + mutex_unlock(&dev->object_name_lock); goto out; } - mutex_lock(&file_priv->prime.lock); - /* if we've exported this buffer the cheat and add it to the import list - * so we get the correct handle back +out_have_obj: + /* + * If we've exported this buffer then cheat and add it to the import list + * so we get the correct handle back. We must do this under the + * protection of dev->object_name_lock to ensure that a racing gem close + * ioctl doesn't miss to remove this buffer handle from the cache. */ ret = drm_prime_add_buf_handle(&file_priv->prime, dmabuf, handle); + mutex_unlock(&dev->object_name_lock); if (ret) goto fail_put_dmabuf; +out_have_handle: ret = dma_buf_fd(dmabuf, flags); - if (ret < 0) - goto fail_rm_handle; - - *prime_fd = ret; - mutex_unlock(&file_priv->prime.lock); - return 0; - -out_have_obj: - ret = dma_buf_fd(dmabuf, flags); + /* + * We must _not_ remove the buffer from the handle cache since the newly + * created dma buf is already linked in the global obj->dma_buf pointer, + * and that is invariant as long as a userspace gem handle exists. + * Closing the handle will clean out the cache anyway, so we don't leak. + */ if (ret < 0) { - dma_buf_put(dmabuf); + goto fail_put_dmabuf; } else { *prime_fd = ret; ret = 0; @@ -406,14 +430,13 @@ out_have_obj: goto out; -fail_rm_handle: - drm_prime_remove_buf_handle_locked(&file_priv->prime, - dmabuf); - mutex_unlock(&file_priv->prime.lock); fail_put_dmabuf: dma_buf_put(dmabuf); out: drm_gem_object_unreference_unlocked(obj); +out_unlock: + mutex_unlock(&file_priv->prime.lock); + return ret; } EXPORT_SYMBOL(drm_gem_prime_handle_to_fd); @@ -669,11 +692,3 @@ void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv) WARN_ON(!list_empty(&prime_fpriv->head)); } EXPORT_SYMBOL(drm_prime_destroy_file_private); - -void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf) -{ - mutex_lock(&prime_fpriv->lock); - drm_prime_remove_buf_handle_locked(prime_fpriv, dma_buf); - mutex_unlock(&prime_fpriv->lock); -} -EXPORT_SYMBOL(drm_prime_remove_buf_handle); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 5914cc5c3fa6..90833dccc919 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1508,7 +1508,7 @@ int drm_gem_dumb_destroy(struct drm_file *file, void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv); void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); -void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf); +void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf); #if DRM_DEBUG_CODE extern int drm_vma_info(struct seq_file *m, void *data); -- cgit v1.2.3-71-gd317 From dbe34724c08ea25d39d31735120077013fbbb6fb Mon Sep 17 00:00:00 2001 From: Mugunthan V N Date: Mon, 19 Aug 2013 17:47:40 +0530 Subject: drivers: net: cpsw: remove platform data header file of cpsw CPSW driver no longer supports platform register as all the SoCs which has CPSW are supporting DT only booting, so moving cpsw.h header file from platform include to drivers/net/ethernet/ti Signed-off-by: Mugunthan V N Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/cpsw.c | 2 +- drivers/net/ethernet/ti/cpsw.h | 42 ++++++++++++++++++++++++++++++++++++ include/linux/platform_data/cpsw.h | 44 -------------------------------------- 3 files changed, 43 insertions(+), 45 deletions(-) create mode 100644 drivers/net/ethernet/ti/cpsw.h delete mode 100644 include/linux/platform_data/cpsw.h (limited to 'include') diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 0fcf21254ad3..b4a85f5531ed 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -34,9 +34,9 @@ #include #include -#include #include +#include "cpsw.h" #include "cpsw_ale.h" #include "cpts.h" #include "davinci_cpdma.h" diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h new file mode 100644 index 000000000000..eb3e101ec048 --- /dev/null +++ b/drivers/net/ethernet/ti/cpsw.h @@ -0,0 +1,42 @@ +/* Texas Instruments Ethernet Switch Driver + * + * Copyright (C) 2013 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __CPSW_H__ +#define __CPSW_H__ + +#include + +struct cpsw_slave_data { + char phy_id[MII_BUS_ID_SIZE]; + int phy_if; + u8 mac_addr[ETH_ALEN]; + u16 dual_emac_res_vlan; /* Reserved VLAN for DualEMAC */ +}; + +struct cpsw_platform_data { + struct cpsw_slave_data *slave_data; + u32 ss_reg_ofs; /* Subsystem control register offset */ + u32 channels; /* number of cpdma channels (symmetric) */ + u32 slaves; /* number of slave cpgmac ports */ + u32 active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */ + u32 cpts_clock_mult; /* convert input clock ticks to nanoseconds */ + u32 cpts_clock_shift; /* convert input clock ticks to nanoseconds */ + u32 ale_entries; /* ale table size */ + u32 bd_ram_size; /*buffer descriptor ram size */ + u32 rx_descs; /* Number of Rx Descriptios */ + u32 mac_control; /* Mac control register */ + u16 default_vlan; /* Def VLAN for ALE lookup in VLAN aware mode*/ + bool dual_emac; /* Enable Dual EMAC mode */ +}; + +#endif /* __CPSW_H__ */ diff --git a/include/linux/platform_data/cpsw.h b/include/linux/platform_data/cpsw.h deleted file mode 100644 index bb3cd58d71e3..000000000000 --- a/include/linux/platform_data/cpsw.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Texas Instruments Ethernet Switch Driver - * - * Copyright (C) 2012 Texas Instruments - * - * 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. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef __CPSW_H__ -#define __CPSW_H__ - -#include - -struct cpsw_slave_data { - char phy_id[MII_BUS_ID_SIZE]; - int phy_if; - u8 mac_addr[ETH_ALEN]; - u16 dual_emac_res_vlan; /* Reserved VLAN for DualEMAC */ - -}; - -struct cpsw_platform_data { - u32 ss_reg_ofs; /* Subsystem control register offset */ - u32 channels; /* number of cpdma channels (symmetric) */ - u32 slaves; /* number of slave cpgmac ports */ - struct cpsw_slave_data *slave_data; - u32 active_slave; /* time stamping, ethtool and SIOCGMIIPHY slave */ - u32 cpts_clock_mult; /* convert input clock ticks to nanoseconds */ - u32 cpts_clock_shift; /* convert input clock ticks to nanoseconds */ - u32 ale_entries; /* ale table size */ - u32 bd_ram_size; /*buffer descriptor ram size */ - u32 rx_descs; /* Number of Rx Descriptios */ - u32 mac_control; /* Mac control register */ - u16 default_vlan; /* Def VLAN for ALE lookup in VLAN aware mode*/ - bool dual_emac; /* Enable Dual EMAC mode */ -}; - -#endif /* __CPSW_H__ */ -- cgit v1.2.3-71-gd317 From 1ddff7da0faecffdcdeab3d981fb8241453cea44 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 19 Aug 2013 15:39:19 +0400 Subject: can: mcp251x: Replace power callbacks with regulator API This patch replaces power callbacks to the regulator API. To improve the readability of the code, helper for the regulator enable/disable was added. Acked-by: Haojian Zhuang Signed-off-by: Alexander Shiyan Signed-off-by: Marc Kleine-Budde --- arch/arm/mach-pxa/icontrol.c | 3 -- arch/arm/mach-pxa/zeus.c | 46 +++++++++++--------- drivers/net/can/mcp251x.c | 83 +++++++++++++++++++----------------- include/linux/can/platform/mcp251x.h | 13 +----- 4 files changed, 69 insertions(+), 76 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-pxa/icontrol.c b/arch/arm/mach-pxa/icontrol.c index fe31bfcbb8df..c98511c5abd1 100644 --- a/arch/arm/mach-pxa/icontrol.c +++ b/arch/arm/mach-pxa/icontrol.c @@ -73,9 +73,6 @@ static struct pxa2xx_spi_chip mcp251x_chip_info4 = { static struct mcp251x_platform_data mcp251x_info = { .oscillator_frequency = 16E6, - .board_specific_setup = NULL, - .power_enable = NULL, - .transceiver_enable = NULL }; static struct spi_board_info mcp251x_board_info[] = { diff --git a/arch/arm/mach-pxa/zeus.c b/arch/arm/mach-pxa/zeus.c index f5d436434566..04a0aea23873 100644 --- a/arch/arm/mach-pxa/zeus.c +++ b/arch/arm/mach-pxa/zeus.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -391,33 +393,34 @@ static struct pxa2xx_spi_master pxa2xx_spi_ssp3_master_info = { }; /* CAN bus on SPI */ -static int zeus_mcp2515_setup(struct spi_device *sdev) -{ - int err; - - err = gpio_request(ZEUS_CAN_SHDN_GPIO, "CAN shutdown"); - if (err) - return err; +static struct regulator_consumer_supply can_regulator_consumer = + REGULATOR_SUPPLY("vdd", "spi3.0"); - err = gpio_direction_output(ZEUS_CAN_SHDN_GPIO, 1); - if (err) { - gpio_free(ZEUS_CAN_SHDN_GPIO); - return err; - } +static struct regulator_init_data can_regulator_init_data = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .consumer_supplies = &can_regulator_consumer, + .num_consumer_supplies = 1, +}; - return 0; -} +static struct fixed_voltage_config can_regulator_pdata = { + .supply_name = "CAN_SHDN", + .microvolts = 3300000, + .gpio = ZEUS_CAN_SHDN_GPIO, + .init_data = &can_regulator_init_data, +}; -static int zeus_mcp2515_transceiver_enable(int enable) -{ - gpio_set_value(ZEUS_CAN_SHDN_GPIO, !enable); - return 0; -} +static struct platform_device can_regulator_device = { + .name = "reg-fixed-volage", + .id = -1, + .dev = { + .platform_data = &can_regulator_pdata, + }, +}; static struct mcp251x_platform_data zeus_mcp2515_pdata = { .oscillator_frequency = 16*1000*1000, - .board_specific_setup = zeus_mcp2515_setup, - .power_enable = zeus_mcp2515_transceiver_enable, }; static struct spi_board_info zeus_spi_board_info[] = { @@ -516,6 +519,7 @@ static struct platform_device *zeus_devices[] __initdata = { &zeus_leds_device, &zeus_pcmcia_device, &zeus_max6369_device, + &can_regulator_device, }; /* AC'97 */ diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c index 8cda23bf0614..d2c54888b211 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/mcp251x.c @@ -37,9 +37,6 @@ * * static struct mcp251x_platform_data mcp251x_info = { * .oscillator_frequency = 8000000, - * .board_specific_setup = &mcp251x_setup, - * .power_enable = mcp251x_power_enable, - * .transceiver_enable = NULL, * }; * * static struct spi_board_info spi_board_info[] = { @@ -76,6 +73,7 @@ #include #include #include +#include /* SPI interface instruction set */ #define INSTRUCTION_WRITE 0x02 @@ -264,6 +262,8 @@ struct mcp251x_priv { #define AFTER_SUSPEND_POWER 4 #define AFTER_SUSPEND_RESTART 8 int restart_tx; + struct regulator *power; + struct regulator *transceiver; }; #define MCP251X_IS(_model) \ @@ -667,16 +667,25 @@ static int mcp251x_hw_probe(struct spi_device *spi) return (st1 == 0x80 && st2 == 0x07) ? 1 : 0; } +static int mcp251x_power_enable(struct regulator *reg, int enable) +{ + if (IS_ERR(reg)) + return 0; + + if (enable) + return regulator_enable(reg); + else + return regulator_disable(reg); +} + static void mcp251x_open_clean(struct net_device *net) { struct mcp251x_priv *priv = netdev_priv(net); struct spi_device *spi = priv->spi; - struct mcp251x_platform_data *pdata = spi->dev.platform_data; free_irq(spi->irq, priv); mcp251x_hw_sleep(spi); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); + mcp251x_power_enable(priv->transceiver, 0); close_candev(net); } @@ -684,7 +693,6 @@ static int mcp251x_stop(struct net_device *net) { struct mcp251x_priv *priv = netdev_priv(net); struct spi_device *spi = priv->spi; - struct mcp251x_platform_data *pdata = spi->dev.platform_data; close_candev(net); @@ -704,8 +712,7 @@ static int mcp251x_stop(struct net_device *net) mcp251x_hw_sleep(spi); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); + mcp251x_power_enable(priv->transceiver, 0); priv->can.state = CAN_STATE_STOPPED; @@ -939,8 +946,7 @@ static int mcp251x_open(struct net_device *net) } mutex_lock(&priv->mcp_lock); - if (pdata->transceiver_enable) - pdata->transceiver_enable(1); + mcp251x_power_enable(priv->transceiver, 1); priv->force_quit = 0; priv->tx_skb = NULL; @@ -956,8 +962,7 @@ static int mcp251x_open(struct net_device *net) flags, DEVICE_NAME, priv); if (ret) { dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); + mcp251x_power_enable(priv->transceiver, 0); close_candev(net); goto open_unlock; } @@ -1026,6 +1031,19 @@ static int mcp251x_can_probe(struct spi_device *spi) CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY; priv->model = spi_get_device_id(spi)->driver_data; priv->net = net; + + priv->power = devm_regulator_get(&spi->dev, "vdd"); + priv->transceiver = devm_regulator_get(&spi->dev, "xceiver"); + if ((PTR_ERR(priv->power) == -EPROBE_DEFER) || + (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) { + ret = -EPROBE_DEFER; + goto error_power; + } + + ret = mcp251x_power_enable(priv->power, 1); + if (ret) + goto error_power; + spi_set_drvdata(spi, priv); priv->spi = spi; @@ -1068,13 +1086,6 @@ static int mcp251x_can_probe(struct spi_device *spi) } } - if (pdata->power_enable) - pdata->power_enable(1); - - /* Call out to platform specific setup */ - if (pdata->board_specific_setup) - pdata->board_specific_setup(spi); - SET_NETDEV_DEV(net, &spi->dev); /* Configure the SPI bus */ @@ -1084,14 +1095,11 @@ static int mcp251x_can_probe(struct spi_device *spi) /* Here is OK to not lock the MCP, no one knows about it yet */ if (!mcp251x_hw_probe(spi)) { - dev_info(&spi->dev, "Probe failed\n"); + ret = -ENODEV; goto error_probe; } mcp251x_hw_sleep(spi); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); - ret = register_candev(net); if (ret) goto error_probe; @@ -1109,13 +1117,13 @@ error_rx_buf: if (!mcp251x_enable_dma) kfree(priv->spi_tx_buf); error_tx_buf: - free_candev(net); if (mcp251x_enable_dma) dma_free_coherent(&spi->dev, PAGE_SIZE, priv->spi_tx_buf, priv->spi_tx_dma); + mcp251x_power_enable(priv->power, 0); +error_power: + free_candev(net); error_alloc: - if (pdata->power_enable) - pdata->power_enable(0); dev_err(&spi->dev, "probe failed\n"); error_out: return ret; @@ -1123,12 +1131,10 @@ error_out: static int mcp251x_can_remove(struct spi_device *spi) { - struct mcp251x_platform_data *pdata = spi->dev.platform_data; struct mcp251x_priv *priv = spi_get_drvdata(spi); struct net_device *net = priv->net; unregister_candev(net); - free_candev(net); if (mcp251x_enable_dma) { dma_free_coherent(&spi->dev, PAGE_SIZE, @@ -1138,8 +1144,9 @@ static int mcp251x_can_remove(struct spi_device *spi) kfree(priv->spi_rx_buf); } - if (pdata->power_enable) - pdata->power_enable(0); + mcp251x_power_enable(priv->power, 0); + + free_candev(net); return 0; } @@ -1149,7 +1156,6 @@ static int mcp251x_can_remove(struct spi_device *spi) static int mcp251x_can_suspend(struct device *dev) { struct spi_device *spi = to_spi_device(dev); - struct mcp251x_platform_data *pdata = spi->dev.platform_data; struct mcp251x_priv *priv = spi_get_drvdata(spi); struct net_device *net = priv->net; @@ -1163,15 +1169,14 @@ static int mcp251x_can_suspend(struct device *dev) netif_device_detach(net); mcp251x_hw_sleep(spi); - if (pdata->transceiver_enable) - pdata->transceiver_enable(0); + mcp251x_power_enable(priv->transceiver, 0); priv->after_suspend = AFTER_SUSPEND_UP; } else { priv->after_suspend = AFTER_SUSPEND_DOWN; } - if (pdata->power_enable) { - pdata->power_enable(0); + if (!IS_ERR(priv->power)) { + regulator_disable(priv->power); priv->after_suspend |= AFTER_SUSPEND_POWER; } @@ -1181,16 +1186,14 @@ static int mcp251x_can_suspend(struct device *dev) static int mcp251x_can_resume(struct device *dev) { struct spi_device *spi = to_spi_device(dev); - struct mcp251x_platform_data *pdata = spi->dev.platform_data; struct mcp251x_priv *priv = spi_get_drvdata(spi); if (priv->after_suspend & AFTER_SUSPEND_POWER) { - pdata->power_enable(1); + mcp251x_power_enable(priv->power, 1); queue_work(priv->wq, &priv->restart_work); } else { if (priv->after_suspend & AFTER_SUSPEND_UP) { - if (pdata->transceiver_enable) - pdata->transceiver_enable(1); + mcp251x_power_enable(priv->transceiver, 1); queue_work(priv->wq, &priv->restart_work); } else { priv->after_suspend = 0; diff --git a/include/linux/can/platform/mcp251x.h b/include/linux/can/platform/mcp251x.h index 089fe43211a4..8a2725676d8b 100644 --- a/include/linux/can/platform/mcp251x.h +++ b/include/linux/can/platform/mcp251x.h @@ -9,26 +9,15 @@ #include -/** +/* * struct mcp251x_platform_data - MCP251X SPI CAN controller platform data * @oscillator_frequency: - oscillator frequency in Hz * @irq_flags: - IRQF configuration flags - * @board_specific_setup: - called before probing the chip (power,reset) - * @transceiver_enable: - called to power on/off the transceiver - * @power_enable: - called to power on/off the mcp *and* the - * transceiver - * - * Please note that you should define power_enable or transceiver_enable or - * none of them. Defining both of them is no use. - * */ struct mcp251x_platform_data { unsigned long oscillator_frequency; unsigned long irq_flags; - int (*board_specific_setup)(struct spi_device *spi); - int (*transceiver_enable)(int enable); - int (*power_enable) (int enable); }; #endif /* __CAN_PLATFORM_MCP251X_H__ */ -- cgit v1.2.3-71-gd317 From ae5d589e5f9f3217656ada632869968178886ac6 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Mon, 19 Aug 2013 15:39:20 +0400 Subject: can: mcp251x: Eliminate irq_flags from driver platform_data Flags is not used by boards, so remove this field from the driver platform_data. Signed-off-by: Alexander Shiyan Signed-off-by: Marc Kleine-Budde --- drivers/net/can/mcp251x.c | 9 +-------- include/linux/can/platform/mcp251x.h | 2 -- 2 files changed, 1 insertion(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c index d2c54888b211..ec84ce163608 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/mcp251x.c @@ -935,8 +935,7 @@ static int mcp251x_open(struct net_device *net) { struct mcp251x_priv *priv = netdev_priv(net); struct spi_device *spi = priv->spi; - struct mcp251x_platform_data *pdata = spi->dev.platform_data; - unsigned long flags; + unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_FALLING; int ret; ret = open_candev(net); @@ -952,12 +951,6 @@ static int mcp251x_open(struct net_device *net) priv->tx_skb = NULL; priv->tx_len = 0; - flags = IRQF_ONESHOT; - if (pdata->irq_flags) - flags |= pdata->irq_flags; - else - flags |= IRQF_TRIGGER_FALLING; - ret = request_threaded_irq(spi->irq, NULL, mcp251x_can_ist, flags, DEVICE_NAME, priv); if (ret) { diff --git a/include/linux/can/platform/mcp251x.h b/include/linux/can/platform/mcp251x.h index 8a2725676d8b..dc029dba7a03 100644 --- a/include/linux/can/platform/mcp251x.h +++ b/include/linux/can/platform/mcp251x.h @@ -12,12 +12,10 @@ /* * struct mcp251x_platform_data - MCP251X SPI CAN controller platform data * @oscillator_frequency: - oscillator frequency in Hz - * @irq_flags: - IRQF configuration flags */ struct mcp251x_platform_data { unsigned long oscillator_frequency; - unsigned long irq_flags; }; #endif /* __CAN_PLATFORM_MCP251X_H__ */ -- cgit v1.2.3-71-gd317 From 183912d352a242a276a7877852f107459a13aff9 Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Thu, 15 Aug 2013 14:01:40 +0100 Subject: of: move of_get_cpu_node implementation to DT core library This patch moves the generalized implementation of of_get_cpu_node from PowerPC to DT core library, thereby adding support for retrieving cpu node for a given logical cpu index on any architecture. The CPU subsystem can now use this function to assign of_node in the cpu device while registering CPUs. It is recommended to use these helper function only in pre-SMP/early initialisation stages to retrieve CPU device node pointers in logical ordering. Once the cpu devices are registered, it can be retrieved easily from cpu device of_node which avoids unnecessary parsing and matching. Cc: Benjamin Herrenschmidt Cc: Grant Likely Acked-by: Rob Herring Signed-off-by: Sudeep KarkadaNagesha --- arch/powerpc/include/asm/prom.h | 3 -- arch/powerpc/kernel/prom.c | 57 ------------------------- drivers/of/base.c | 95 +++++++++++++++++++++++++++++++++++++++++ include/linux/cpu.h | 1 + include/linux/of.h | 7 +++ 5 files changed, 103 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/arch/powerpc/include/asm/prom.h b/arch/powerpc/include/asm/prom.h index bc2da154f68b..ac204e022922 100644 --- a/arch/powerpc/include/asm/prom.h +++ b/arch/powerpc/include/asm/prom.h @@ -43,9 +43,6 @@ void of_parse_dma_window(struct device_node *dn, const void *dma_window_prop, extern void kdump_move_device_tree(void); -/* CPU OF node matching */ -struct device_node *of_get_cpu_node(int cpu, unsigned int *thread); - /* cache lookup */ struct device_node *of_find_next_cache_node(struct device_node *np); diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index f7b8c0be982e..1c14cd4a5e05 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -870,63 +870,6 @@ bool arch_match_cpu_phys_id(int cpu, u64 phys_id) return (int)phys_id == get_hard_smp_processor_id(cpu); } -static bool __of_find_n_match_cpu_property(struct device_node *cpun, - const char *prop_name, int cpu, unsigned int *thread) -{ - const __be32 *cell; - int ac, prop_len, tid; - u64 hwid; - - ac = of_n_addr_cells(cpun); - cell = of_get_property(cpun, prop_name, &prop_len); - if (!cell) - return false; - prop_len /= sizeof(*cell); - for (tid = 0; tid < prop_len; tid++) { - hwid = of_read_number(cell, ac); - if (arch_match_cpu_phys_id(cpu, hwid)) { - if (thread) - *thread = tid; - return true; - } - cell += ac; - } - return false; -} - -/* Find the device node for a given logical cpu number, also returns the cpu - * local thread number (index in ibm,interrupt-server#s) if relevant and - * asked for (non NULL) - */ -struct device_node *of_get_cpu_node(int cpu, unsigned int *thread) -{ - struct device_node *cpun, *cpus; - - cpus = of_find_node_by_path("/cpus"); - if (!cpus) { - pr_warn("Missing cpus node, bailing out\n"); - return NULL; - } - - for_each_child_of_node(cpus, cpun) { - if (of_node_cmp(cpun->type, "cpu")) - continue; - - /* Check for non-standard "ibm,ppc-interrupt-server#s" property - * for thread ids on PowerPC. If it doesn't exist fallback to - * standard "reg" property. - */ - if (__of_find_n_match_cpu_property(cpun, - "ibm,ppc-interrupt-server#s", cpu, thread)) - return cpun; - - if (__of_find_n_match_cpu_property(cpun, "reg", cpu, thread)) - return cpun; - } - return NULL; -} -EXPORT_SYMBOL(of_get_cpu_node); - #if defined(CONFIG_DEBUG_FS) && defined(DEBUG) static struct debugfs_blob_wrapper flat_dt_blob; diff --git a/drivers/of/base.c b/drivers/of/base.c index 5c5427918eb2..605afa9fbe5e 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -18,6 +18,7 @@ * 2 of the License, or (at your option) any later version. */ #include +#include #include #include #include @@ -230,6 +231,100 @@ const void *of_get_property(const struct device_node *np, const char *name, } EXPORT_SYMBOL(of_get_property); +/* + * arch_match_cpu_phys_id - Match the given logical CPU and physical id + * + * @cpu: logical cpu index of a core/thread + * @phys_id: physical identifier of a core/thread + * + * CPU logical to physical index mapping is architecture specific. + * However this __weak function provides a default match of physical + * id to logical cpu index. phys_id provided here is usually values read + * from the device tree which must match the hardware internal registers. + * + * Returns true if the physical identifier and the logical cpu index + * correspond to the same core/thread, false otherwise. + */ +bool __weak arch_match_cpu_phys_id(int cpu, u64 phys_id) +{ + return (u32)phys_id == cpu; +} + +/** + * Checks if the given "prop_name" property holds the physical id of the + * core/thread corresponding to the logical cpu 'cpu'. If 'thread' is not + * NULL, local thread number within the core is returned in it. + */ +static bool __of_find_n_match_cpu_property(struct device_node *cpun, + const char *prop_name, int cpu, unsigned int *thread) +{ + const __be32 *cell; + int ac, prop_len, tid; + u64 hwid; + + ac = of_n_addr_cells(cpun); + cell = of_get_property(cpun, prop_name, &prop_len); + if (!cell) + return false; + prop_len /= sizeof(*cell); + for (tid = 0; tid < prop_len; tid++) { + hwid = of_read_number(cell, ac); + if (arch_match_cpu_phys_id(cpu, hwid)) { + if (thread) + *thread = tid; + return true; + } + cell += ac; + } + return false; +} + +/** + * of_get_cpu_node - Get device node associated with the given logical CPU + * + * @cpu: CPU number(logical index) for which device node is required + * @thread: if not NULL, local thread number within the physical core is + * returned + * + * The main purpose of this function is to retrieve the device node for the + * given logical CPU index. It should be used to initialize the of_node in + * cpu device. Once of_node in cpu device is populated, all the further + * references can use that instead. + * + * CPU logical to physical index mapping is architecture specific and is built + * before booting secondary cores. This function uses arch_match_cpu_phys_id + * which can be overridden by architecture specific implementation. + * + * Returns a node pointer for the logical cpu if found, else NULL. + */ +struct device_node *of_get_cpu_node(int cpu, unsigned int *thread) +{ + struct device_node *cpun, *cpus; + + cpus = of_find_node_by_path("/cpus"); + if (!cpus) { + pr_warn("Missing cpus node, bailing out\n"); + return NULL; + } + + for_each_child_of_node(cpus, cpun) { + if (of_node_cmp(cpun->type, "cpu")) + continue; + /* Check for non-standard "ibm,ppc-interrupt-server#s" property + * for thread ids on PowerPC. If it doesn't exist fallback to + * standard "reg" property. + */ + if (IS_ENABLED(CONFIG_PPC) && + __of_find_n_match_cpu_property(cpun, + "ibm,ppc-interrupt-server#s", cpu, thread)) + return cpun; + if (__of_find_n_match_cpu_property(cpun, "reg", cpu, thread)) + return cpun; + } + return NULL; +} +EXPORT_SYMBOL(of_get_cpu_node); + /** Checks if the given "compat" string matches one of the strings in * the device's "compatible" property */ diff --git a/include/linux/cpu.h b/include/linux/cpu.h index ab0eade73039..3dfed2b9ae2e 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -28,6 +28,7 @@ struct cpu { extern int register_cpu(struct cpu *cpu, int num); extern struct device *get_cpu_device(unsigned cpu); extern bool cpu_is_hotpluggable(unsigned cpu); +extern bool arch_match_cpu_phys_id(int cpu, u64 phys_id); extern int cpu_add_dev_attr(struct device_attribute *attr); extern void cpu_remove_dev_attr(struct device_attribute *attr); diff --git a/include/linux/of.h b/include/linux/of.h index 1fd08ca23106..c0bb2f188048 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -266,6 +266,7 @@ extern int of_device_is_available(const struct device_node *device); extern const void *of_get_property(const struct device_node *node, const char *name, int *lenp); +extern struct device_node *of_get_cpu_node(int cpu, unsigned int *thread); #define for_each_property_of_node(dn, pp) \ for (pp = dn->properties; pp != NULL; pp = pp->next) @@ -459,6 +460,12 @@ static inline const void *of_get_property(const struct device_node *node, return NULL; } +static inline struct device_node *of_get_cpu_node(int cpu, + unsigned int *thread) +{ + return NULL; +} + static inline int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value) { -- cgit v1.2.3-71-gd317 From bd00860e960e52658cebfd65eeb4748ed6ff039f Mon Sep 17 00:00:00 2001 From: Sudeep KarkadaNagesha Date: Thu, 18 Jul 2013 11:22:04 +0100 Subject: of/device: add helper to get cpu device node from logical cpu index Multiple drivers need to get the cpu device node from the cpu logical index and then access the of_node. This patch adds helper function to fetch the device node directly. Acked-by: Rob Herring Signed-off-by: Sudeep KarkadaNagesha --- include/linux/of_device.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'include') diff --git a/include/linux/of_device.h b/include/linux/of_device.h index 9d27475feec1..82ce324fdce7 100644 --- a/include/linux/of_device.h +++ b/include/linux/of_device.h @@ -1,6 +1,7 @@ #ifndef _LINUX_OF_DEVICE_H #define _LINUX_OF_DEVICE_H +#include #include #include /* temporary until merge */ @@ -43,6 +44,15 @@ static inline void of_device_node_put(struct device *dev) of_node_put(dev->of_node); } +static inline struct device_node *of_cpu_device_node_get(int cpu) +{ + struct device *cpu_dev; + cpu_dev = get_cpu_device(cpu); + if (!cpu_dev) + return NULL; + return of_node_get(cpu_dev->of_node); +} + #else /* CONFIG_OF */ static inline int of_driver_match_device(struct device *dev, @@ -67,6 +77,11 @@ static inline const struct of_device_id *of_match_device( { return NULL; } + +static inline struct device_node *of_cpu_device_node_get(int cpu) +{ + return NULL; +} #endif /* CONFIG_OF */ #endif /* _LINUX_OF_DEVICE_H */ -- cgit v1.2.3-71-gd317 From 257aff515409f9455edff3a946344e71baf9e116 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Sat, 17 Aug 2013 21:42:22 -0500 Subject: crypto: scatterwalk - Add support for calculating number of SG elements Crypto layer only passes nbytes to encrypt but in omap-aes driver we need to know number of SG elements to pass to dmaengine slave API. We add function for the same to scatterwalk library. Signed-off-by: Joel Fernandes Signed-off-by: Herbert Xu --- crypto/scatterwalk.c | 22 ++++++++++++++++++++++ include/crypto/scatterwalk.h | 2 ++ 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/crypto/scatterwalk.c b/crypto/scatterwalk.c index 7281b8a93ad3..79ca2278c2a3 100644 --- a/crypto/scatterwalk.c +++ b/crypto/scatterwalk.c @@ -124,3 +124,25 @@ void scatterwalk_map_and_copy(void *buf, struct scatterlist *sg, scatterwalk_done(&walk, out, 0); } EXPORT_SYMBOL_GPL(scatterwalk_map_and_copy); + +int scatterwalk_bytes_sglen(struct scatterlist *sg, int num_bytes) +{ + int offset = 0, n = 0; + + /* num_bytes is too small */ + if (num_bytes < sg->length) + return -1; + + do { + offset += sg->length; + n++; + sg = scatterwalk_sg_next(sg); + + /* num_bytes is too large */ + if (unlikely(!sg && (num_bytes < offset))) + return -1; + } while (sg && (num_bytes > offset)); + + return n; +} +EXPORT_SYMBOL_GPL(scatterwalk_bytes_sglen); diff --git a/include/crypto/scatterwalk.h b/include/crypto/scatterwalk.h index 3744d2a642df..13621cc8cf4c 100644 --- a/include/crypto/scatterwalk.h +++ b/include/crypto/scatterwalk.h @@ -113,4 +113,6 @@ void scatterwalk_done(struct scatter_walk *walk, int out, int more); void scatterwalk_map_and_copy(void *buf, struct scatterlist *sg, unsigned int start, unsigned int nbytes, int out); +int scatterwalk_bytes_sglen(struct scatterlist *sg, int num_bytes); + #endif /* _CRYPTO_SCATTERWALK_H */ -- cgit v1.2.3-71-gd317 From e660ed6c70370dae0887e2b5224d8b6c1e138120 Mon Sep 17 00:00:00 2001 From: Frédéric Dalleau Date: Mon, 19 Aug 2013 14:23:54 +0200 Subject: Bluetooth: Use hci_connect_sco directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hci_connect is a super function for connecting hci protocols. But the voice_setting parameter (introduced in subsequent patches) is only needed by SCO and security requirements are not needed for SCO channels. Thus, it makes sense to have a separate function for SCO. Signed-off-by: Frédéric Dalleau Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 8 ++------ net/bluetooth/sco.c | 3 +-- 3 files changed, 5 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f77885ea78c2..307a19269020 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -584,6 +584,8 @@ struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle); struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 dst_type, __u8 sec_level, __u8 auth_type); +struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, + bdaddr_t *dst); int hci_conn_check_link_mode(struct hci_conn *conn); int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level); int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 6c7f36379722..5f1f448dd0f2 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -560,13 +560,12 @@ static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, return acl; } -static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, - bdaddr_t *dst, u8 sec_level, u8 auth_type) +struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst) { struct hci_conn *acl; struct hci_conn *sco; - acl = hci_connect_acl(hdev, dst, sec_level, auth_type); + acl = hci_connect_acl(hdev, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING); if (IS_ERR(acl)) return acl; @@ -612,9 +611,6 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, return hci_connect_le(hdev, dst, dst_type, sec_level, auth_type); case ACL_LINK: return hci_connect_acl(hdev, dst, sec_level, auth_type); - case SCO_LINK: - case ESCO_LINK: - return hci_connect_sco(hdev, type, dst, sec_level, auth_type); } return ERR_PTR(-EINVAL); diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 2de7150a6304..ab2502cbecfb 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -176,8 +176,7 @@ static int sco_connect(struct sock *sk) else type = SCO_LINK; - hcon = hci_connect(hdev, type, dst, BDADDR_BREDR, BT_SECURITY_LOW, - HCI_AT_NO_BONDING); + hcon = hci_connect_sco(hdev, type, dst); if (IS_ERR(hcon)) { err = PTR_ERR(hcon); goto done; -- cgit v1.2.3-71-gd317 From ad10b1a48754b1381582d96f070a39832e41382d Mon Sep 17 00:00:00 2001 From: Frédéric Dalleau Date: Mon, 19 Aug 2013 14:23:56 +0200 Subject: Bluetooth: Add Bluetooth socket voice option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch extends the current Bluetooth socket options with BT_VOICE. This is intended to choose voice data type at runtime. It only applies to SCO sockets. Incoming connections shall be setup during deferred setup. Outgoing connections shall be setup before connect(). The desired setting is stored in the SCO socket info. This patch declares needed members, modifies getsockopt() and setsockopt(). Signed-off-by: Frédéric Dalleau Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- include/net/bluetooth/bluetooth.h | 8 ++++++++ include/net/bluetooth/sco.h | 1 + net/bluetooth/sco.c | 40 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 10eb9b389014..10d43d8c7037 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -107,6 +107,14 @@ struct bt_power { */ #define BT_CHANNEL_POLICY_AMP_PREFERRED 2 +#define BT_VOICE 11 +struct bt_voice { + __u16 setting; +}; + +#define BT_VOICE_TRANSPARENT 0x0003 +#define BT_VOICE_CVSD_16BIT 0x0060 + __printf(1, 2) int bt_info(const char *fmt, ...); __printf(1, 2) diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index 1e35c43657c8..e252a31ee6b6 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -73,6 +73,7 @@ struct sco_conn { struct sco_pinfo { struct bt_sock bt; __u32 flags; + __u16 setting; struct sco_conn *conn; }; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index acdca68806db..678747e2e389 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -416,6 +416,8 @@ static struct sock *sco_sock_alloc(struct net *net, struct socket *sock, int pro sk->sk_protocol = proto; sk->sk_state = BT_OPEN; + sco_pi(sk)->setting = BT_VOICE_CVSD_16BIT; + setup_timer(&sk->sk_timer, sco_sock_timeout, (unsigned long)sk); bt_sock_link(&sco_sk_list, sk); @@ -709,7 +711,8 @@ static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock, static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; - int err = 0; + int len, err = 0; + struct bt_voice voice; u32 opt; BT_DBG("sk %p", sk); @@ -735,6 +738,31 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); break; + case BT_VOICE: + if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND && + sk->sk_state != BT_CONNECT2) { + err = -EINVAL; + break; + } + + voice.setting = sco_pi(sk)->setting; + + len = min_t(unsigned int, sizeof(voice), optlen); + if (copy_from_user((char *) &voice, optval, len)) { + err = -EFAULT; + break; + } + + /* Explicitly check for these values */ + if (voice.setting != BT_VOICE_TRANSPARENT && + voice.setting != BT_VOICE_CVSD_16BIT) { + err = -EINVAL; + break; + } + + sco_pi(sk)->setting = voice.setting; + break; + default: err = -ENOPROTOOPT; break; @@ -808,6 +836,7 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char { struct sock *sk = sock->sk; int len, err = 0; + struct bt_voice voice; BT_DBG("sk %p", sk); @@ -833,6 +862,15 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char break; + case BT_VOICE: + voice.setting = sco_pi(sk)->setting; + + len = min_t(unsigned int, len, sizeof(voice)); + if (copy_to_user(optval, (char *)&voice, len)) + err = -EFAULT; + + break; + default: err = -ENOPROTOOPT; break; -- cgit v1.2.3-71-gd317 From 5d4d62f6ca04c54ed6c84df6adf5427c52feda3e Mon Sep 17 00:00:00 2001 From: Frédéric Dalleau Date: Mon, 19 Aug 2013 14:23:57 +0200 Subject: Bluetooth: Add constants for SCO airmode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch defines constants for SCO airmode from SCO voice setting. It refers to Bluetooth Core V4.0 specification, Part E, Chap 6.12 which describe SCO voice setting format. Signed-off-by: Frédéric Dalleau Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 307a19269020..f403509b4d12 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1215,4 +1215,8 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], u8 bdaddr_to_le(u8 bdaddr_type); +#define SCO_AIRMODE_MASK 0x0003 +#define SCO_AIRMODE_CVSD 0x0000 +#define SCO_AIRMODE_TRANSP 0x0003 + #endif /* __HCI_CORE_H */ -- cgit v1.2.3-71-gd317 From 10c62ddc6f032c3a096401ca3ce7e5b2d5780859 Mon Sep 17 00:00:00 2001 From: Frédéric Dalleau Date: Mon, 19 Aug 2013 14:23:59 +0200 Subject: Bluetooth: Parameters for outgoing SCO connections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to establish a transparent SCO connection, the correct settings must be specified in the Setup Synchronous Connection request. For that, a setting field is added to ACL connection data to set up the desired parameters. The patch also removes usage of hdev->voice_setting in CVSD connection and makes use of T2 parameters for transparent data. Signed-off-by: Frédéric Dalleau Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 5 +++-- net/bluetooth/hci_conn.c | 24 +++++++++++++++++++----- net/bluetooth/sco.c | 2 +- 3 files changed, 23 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f403509b4d12..61ca2ce5b063 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -320,6 +320,7 @@ struct hci_conn { __u32 passkey_notify; __u8 passkey_entered; __u16 disc_timeout; + __u16 setting; unsigned long flags; __u8 remote_cap; @@ -584,8 +585,8 @@ struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle); struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 dst_type, __u8 sec_level, __u8 auth_type); -struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, - bdaddr_t *dst); +struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, + __u16 setting); int hci_conn_check_link_mode(struct hci_conn *conn); int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level); int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 5f1f448dd0f2..c0e56a512ff3 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -185,13 +185,24 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle) conn->attempt++; cp.handle = cpu_to_le16(handle); - cp.pkt_type = cpu_to_le16(conn->pkt_type); cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40); cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40); - cp.max_latency = __constant_cpu_to_le16(0xffff); - cp.voice_setting = cpu_to_le16(hdev->voice_setting); - cp.retrans_effort = 0xff; + cp.voice_setting = cpu_to_le16(conn->setting); + + switch (conn->setting & SCO_AIRMODE_MASK) { + case SCO_AIRMODE_TRANSP: + cp.pkt_type = __constant_cpu_to_le16(EDR_ESCO_MASK & + ~ESCO_2EV3); + cp.max_latency = __constant_cpu_to_le16(0x000d); + cp.retrans_effort = 0x02; + break; + case SCO_AIRMODE_CVSD: + cp.pkt_type = cpu_to_le16(conn->pkt_type); + cp.max_latency = __constant_cpu_to_le16(0xffff); + cp.retrans_effort = 0xff; + break; + } hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp); } @@ -560,7 +571,8 @@ static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, return acl; } -struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst) +struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, + __u16 setting) { struct hci_conn *acl; struct hci_conn *sco; @@ -583,6 +595,8 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst) hci_conn_hold(sco); + sco->setting = setting; + if (acl->state == BT_CONNECTED && (sco->state == BT_OPEN || sco->state == BT_CLOSED)) { set_bit(HCI_CONN_POWER_SAVE, &acl->flags); diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index b1016c82c908..ed581b41e034 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -176,7 +176,7 @@ static int sco_connect(struct sock *sk) else type = SCO_LINK; - hcon = hci_connect_sco(hdev, type, dst); + hcon = hci_connect_sco(hdev, type, dst, sco_pi(sk)->setting); if (IS_ERR(hcon)) { err = PTR_ERR(hcon); goto done; -- cgit v1.2.3-71-gd317 From 07a5c61eda7f23883273f738edbf6caaebb71923 Mon Sep 17 00:00:00 2001 From: Frédéric Dalleau Date: Mon, 19 Aug 2013 14:24:00 +0200 Subject: Bluetooth: Add constants and macro declaration for transparent data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch defines constants and macro for transparent data LMP features. It refers to Bluetooth Core V4.0 specification, Part C, Chap 3.3 which defines LMP feature mask. Signed-off-by: Frédéric Dalleau Acked-by: Marcel Holtmann Signed-off-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/hci_core.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index a01fbb4a0499..aaeaf0938ec0 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -238,6 +238,7 @@ enum { #define LMP_CVSD 0x01 #define LMP_PSCHEME 0x02 #define LMP_PCONTROL 0x04 +#define LMP_TRANSPARENT 0x08 #define LMP_RSSI_INQ 0x40 #define LMP_ESCO 0x80 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 61ca2ce5b063..b2bfab801840 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -800,6 +800,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_lsto_capable(dev) ((dev)->features[0][7] & LMP_LSTO) #define lmp_inq_tx_pwr_capable(dev) ((dev)->features[0][7] & LMP_INQ_TX_PWR) #define lmp_ext_feat_capable(dev) ((dev)->features[0][7] & LMP_EXTFEATURES) +#define lmp_transp_capable(dev) ((dev)->features[0][2] & LMP_TRANSPARENT) /* ----- Extended LMP capabilities ----- */ #define lmp_host_ssp_capable(dev) ((dev)->features[1][0] & LMP_HOST_SSP) -- cgit v1.2.3-71-gd317 From 2dea632f9acad076370fe871d4ccc93868621403 Mon Sep 17 00:00:00 2001 From: Frédéric Dalleau Date: Mon, 19 Aug 2013 14:24:03 +0200 Subject: Bluetooth: Add SCO connection fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When initiating a transparent eSCO connection, make use of T2 settings at first try. T2 is the recommended settings from HFP 1.6 WideBand Speech. Upon connection failure, try T1 settings. When CVSD is requested and eSCO is supported, try to establish eSCO connection using S3 settings. If it fails, fallback in sequence to S2, S1, D1, D0 settings. To know which setting should be used, conn->attempt is used. It indicates the currently ongoing SCO connection attempt and can be used as the index for the fallback settings table. These setting and the fallback order are described in Bluetooth HFP 1.6 specification p. 101. Signed-off-by: Frédéric Dalleau Signed-off-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_conn.c | 44 ++++++++++++++++++++++++++++++++-------- net/bluetooth/hci_event.c | 6 +++--- 3 files changed, 40 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b2bfab801840..3ede820d328f 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -570,7 +570,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev, } void hci_disconnect(struct hci_conn *conn, __u8 reason); -void hci_setup_sync(struct hci_conn *conn, __u16 handle); +bool hci_setup_sync(struct hci_conn *conn, __u16 handle); void hci_sco_setup(struct hci_conn *conn, __u8 status); struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index c0e56a512ff3..f0817121ec5e 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -31,6 +31,24 @@ #include #include +struct sco_param { + u16 pkt_type; + u16 max_latency; +}; + +static const struct sco_param sco_param_cvsd[] = { + { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000a }, /* S3 */ + { EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007 }, /* S2 */ + { EDR_ESCO_MASK | ESCO_EV3, 0x0007 }, /* S1 */ + { EDR_ESCO_MASK | ESCO_HV3, 0xffff }, /* D1 */ + { EDR_ESCO_MASK | ESCO_HV1, 0xffff }, /* D0 */ +}; + +static const struct sco_param sco_param_wideband[] = { + { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000d }, /* T2 */ + { EDR_ESCO_MASK | ESCO_EV3, 0x0008 }, /* T1 */ +}; + static void hci_le_create_connection(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; @@ -172,10 +190,11 @@ static void hci_add_sco(struct hci_conn *conn, __u16 handle) hci_send_cmd(hdev, HCI_OP_ADD_SCO, sizeof(cp), &cp); } -void hci_setup_sync(struct hci_conn *conn, __u16 handle) +bool hci_setup_sync(struct hci_conn *conn, __u16 handle) { struct hci_dev *hdev = conn->hdev; struct hci_cp_setup_sync_conn cp; + const struct sco_param *param; BT_DBG("hcon %p", conn); @@ -192,19 +211,28 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle) switch (conn->setting & SCO_AIRMODE_MASK) { case SCO_AIRMODE_TRANSP: - cp.pkt_type = __constant_cpu_to_le16(EDR_ESCO_MASK & - ~ESCO_2EV3); - cp.max_latency = __constant_cpu_to_le16(0x000d); + if (conn->attempt > ARRAY_SIZE(sco_param_wideband)) + return false; cp.retrans_effort = 0x02; + param = &sco_param_wideband[conn->attempt - 1]; break; case SCO_AIRMODE_CVSD: - cp.pkt_type = cpu_to_le16(conn->pkt_type); - cp.max_latency = __constant_cpu_to_le16(0xffff); - cp.retrans_effort = 0xff; + if (conn->attempt > ARRAY_SIZE(sco_param_cvsd)) + return false; + cp.retrans_effort = 0x01; + param = &sco_param_cvsd[conn->attempt - 1]; break; + default: + return false; } - hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp); + cp.pkt_type = __cpu_to_le16(param->pkt_type); + cp.max_latency = __cpu_to_le16(param->max_latency); + + if (hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp) < 0) + return false; + + return true; } void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a8bb7bb3f483..94aab73f89d4 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2909,11 +2909,11 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, case 0x1c: /* SCO interval rejected */ case 0x1a: /* Unsupported Remote Feature */ case 0x1f: /* Unspecified error */ - if (conn->out && conn->attempt < 2) { + if (conn->out) { conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | (hdev->esco_type & EDR_ESCO_MASK); - hci_setup_sync(conn, conn->link->handle); - goto unlock; + if (hci_setup_sync(conn, conn->link->handle)) + goto unlock; } /* fall through */ -- cgit v1.2.3-71-gd317 From 37a7bd6255b415afe197489b5cd1f9568a7ae058 Mon Sep 17 00:00:00 2001 From: Seth Jennings Date: Tue, 20 Aug 2013 12:13:01 -0500 Subject: drivers: base: reduce add_memory_section() for boot-time only Now that add_memory_section() is only called from boot time, reduce the logic and remove the enum. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 41 ++++++++++++++--------------------------- include/linux/memory.h | 1 - 2 files changed, 14 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 2e9a68c64679..a9e9c0902cce 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -602,40 +602,29 @@ static int init_memory_block(struct memory_block **memory, return ret; } -static int add_memory_section(int nid, struct mem_section *section, - struct memory_block **mem_p, - unsigned long state, enum mem_add_context context) +static int add_memory_section(struct mem_section *section, + struct memory_block **mem_p) { struct memory_block *mem = NULL; int scn_nr = __section_nr(section); int ret = 0; - if (context == BOOT) { - /* same memory block ? */ - if (mem_p && *mem_p) - if (scn_nr >= (*mem_p)->start_section_nr && - scn_nr <= (*mem_p)->end_section_nr) { - mem = *mem_p; - get_device(&mem->dev); - } - } else - mem = find_memory_block(section); + if (mem_p && *mem_p) { + if (scn_nr >= (*mem_p)->start_section_nr && + scn_nr <= (*mem_p)->end_section_nr) { + mem = *mem_p; + get_device(&mem->dev); + } + } if (mem) { mem->section_count++; put_device(&mem->dev); } else { - ret = init_memory_block(&mem, section, state); + ret = init_memory_block(&mem, section, MEM_ONLINE); /* store memory_block pointer for next loop */ - if (!ret && context == BOOT) - if (mem_p) - *mem_p = mem; - } - - if (!ret) { - if (context == HOTPLUG && - mem->section_count == sections_per_block) - ret = register_mem_sect_under_node(mem, nid); + if (!ret && mem_p) + *mem_p = mem; } return ret; @@ -764,10 +753,8 @@ int __init memory_dev_init(void) if (!present_section_nr(i)) continue; /* don't need to reuse memory_block if only one per block */ - err = add_memory_section(0, __nr_to_section(i), - (sections_per_block == 1) ? NULL : &mem, - MEM_ONLINE, - BOOT); + err = add_memory_section(__nr_to_section(i), + (sections_per_block == 1) ? NULL : &mem); if (!ret) ret = err; } diff --git a/include/linux/memory.h b/include/linux/memory.h index 85c31a8e2904..4c89fb0c8f4c 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -125,7 +125,6 @@ extern struct memory_block *find_memory_block_hinted(struct mem_section *, struct memory_block *); extern struct memory_block *find_memory_block(struct mem_section *); #define CONFIG_MEM_BLOCK_SIZE (PAGES_PER_SECTION< Date: Tue, 20 Aug 2013 16:05:05 -0500 Subject: drivers: base: use standard device online/offline for state change There are two ways to set the online/offline state for a memory block: echo 0|1 > online and echo online|online_kernel|online_movable|offline > state. The state attribute can online a memory block with extra data, the "online type", where the online attribute uses a default online type of ONLINE_KEEP, same as echo online > state. Currently there is a state_mutex that provides consistency between the memory block state and the underlying memory. The problem is that this code does a lot of things that the common device layer can do for us, such as the serialization of the online/offline handlers using the device lock, setting the dev->offline field, and calling kobject_uevent(). This patch refactors the online/offline code to allow the common device_[online|offline] functions to be used. The result is a simpler and more common code path for the two state setting mechanisms. It also removes the state_mutex from the struct memory_block as the memory block device lock provides the state consistency. No functional change is intended by this patch. Signed-off-by: Seth Jennings Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 127 +++++++++++++++++++++---------------------------- include/linux/memory.h | 13 ++--- 2 files changed, 58 insertions(+), 82 deletions(-) (limited to 'include') diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 0187fe483d7c..2a38cd2da2e5 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -261,9 +260,8 @@ memory_block_action(unsigned long phys_index, unsigned long action, int online_t return ret; } -static int __memory_block_change_state(struct memory_block *mem, - unsigned long to_state, unsigned long from_state_req, - int online_type) +static int memory_block_change_state(struct memory_block *mem, + unsigned long to_state, unsigned long from_state_req) { int ret = 0; @@ -273,105 +271,91 @@ static int __memory_block_change_state(struct memory_block *mem, if (to_state == MEM_OFFLINE) mem->state = MEM_GOING_OFFLINE; - ret = memory_block_action(mem->start_section_nr, to_state, online_type); + ret = memory_block_action(mem->start_section_nr, to_state, + mem->online_type); + mem->state = ret ? from_state_req : to_state; + return ret; } +/* The device lock serializes operations on memory_subsys_[online|offline] */ static int memory_subsys_online(struct device *dev) { struct memory_block *mem = container_of(dev, struct memory_block, dev); int ret; - mutex_lock(&mem->state_mutex); + if (mem->state == MEM_ONLINE) + return 0; - ret = mem->state == MEM_ONLINE ? 0 : - __memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE, - ONLINE_KEEP); + /* + * If we are called from store_mem_state(), online_type will be + * set >= 0 Otherwise we were called from the device online + * attribute and need to set the online_type. + */ + if (mem->online_type < 0) + mem->online_type = ONLINE_KEEP; + + ret = memory_block_change_state(mem, MEM_ONLINE, MEM_OFFLINE); + + /* clear online_type */ + mem->online_type = -1; - mutex_unlock(&mem->state_mutex); return ret; } static int memory_subsys_offline(struct device *dev) { struct memory_block *mem = container_of(dev, struct memory_block, dev); - int ret; - mutex_lock(&mem->state_mutex); - - ret = mem->state == MEM_OFFLINE ? 0 : - __memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE, -1); - - mutex_unlock(&mem->state_mutex); - return ret; -} + if (mem->state == MEM_OFFLINE) + return 0; -static int __memory_block_change_state_uevent(struct memory_block *mem, - unsigned long to_state, unsigned long from_state_req, - int online_type) -{ - int ret = __memory_block_change_state(mem, to_state, from_state_req, - online_type); - if (!ret) { - switch (mem->state) { - case MEM_OFFLINE: - kobject_uevent(&mem->dev.kobj, KOBJ_OFFLINE); - break; - case MEM_ONLINE: - kobject_uevent(&mem->dev.kobj, KOBJ_ONLINE); - break; - default: - break; - } - } - return ret; + return memory_block_change_state(mem, MEM_OFFLINE, MEM_ONLINE); } -static int memory_block_change_state(struct memory_block *mem, - unsigned long to_state, unsigned long from_state_req, - int online_type) -{ - int ret; - - mutex_lock(&mem->state_mutex); - ret = __memory_block_change_state_uevent(mem, to_state, from_state_req, - online_type); - mutex_unlock(&mem->state_mutex); - - return ret; -} static ssize_t store_mem_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct memory_block *mem; - bool offline; - int ret = -EINVAL; + int ret, online_type; mem = container_of(dev, struct memory_block, dev); lock_device_hotplug(); - if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) { - offline = false; - ret = memory_block_change_state(mem, MEM_ONLINE, - MEM_OFFLINE, ONLINE_KERNEL); - } else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) { - offline = false; - ret = memory_block_change_state(mem, MEM_ONLINE, - MEM_OFFLINE, ONLINE_MOVABLE); - } else if (!strncmp(buf, "online", min_t(int, count, 6))) { - offline = false; - ret = memory_block_change_state(mem, MEM_ONLINE, - MEM_OFFLINE, ONLINE_KEEP); - } else if(!strncmp(buf, "offline", min_t(int, count, 7))) { - offline = true; - ret = memory_block_change_state(mem, MEM_OFFLINE, - MEM_ONLINE, -1); + if (!strncmp(buf, "online_kernel", min_t(int, count, 13))) + online_type = ONLINE_KERNEL; + else if (!strncmp(buf, "online_movable", min_t(int, count, 14))) + online_type = ONLINE_MOVABLE; + else if (!strncmp(buf, "online", min_t(int, count, 6))) + online_type = ONLINE_KEEP; + else if (!strncmp(buf, "offline", min_t(int, count, 7))) + online_type = -1; + else + return -EINVAL; + + switch (online_type) { + case ONLINE_KERNEL: + case ONLINE_MOVABLE: + case ONLINE_KEEP: + /* + * mem->online_type is not protected so there can be a + * race here. However, when racing online, the first + * will succeed and the second will just return as the + * block will already be online. The online type + * could be either one, but that is expected. + */ + mem->online_type = online_type; + ret = device_online(&mem->dev); + break; + case -1: + ret = device_offline(&mem->dev); + break; + default: + ret = -EINVAL; /* should never happen */ } - if (!ret) - dev->offline = offline; unlock_device_hotplug(); @@ -592,7 +576,6 @@ static int init_memory_block(struct memory_block **memory, mem->end_section_nr = mem->start_section_nr + sections_per_block - 1; mem->state = state; mem->section_count++; - mutex_init(&mem->state_mutex); start_pfn = section_nr_to_pfn(mem->start_section_nr); mem->phys_device = arch_get_memory_phys_device(start_pfn); diff --git a/include/linux/memory.h b/include/linux/memory.h index 4c89fb0c8f4c..9a6bbf76452d 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -25,16 +25,9 @@ struct memory_block { unsigned long start_section_nr; unsigned long end_section_nr; - unsigned long state; - int section_count; - - /* - * This serializes all state change requests. It isn't - * held during creation because the control files are - * created long after the critical areas during - * initialization. - */ - struct mutex state_mutex; + unsigned long state; /* serialized by the dev->lock */ + int section_count; /* serialized by mem_sysfs_mutex */ + int online_type; /* for passing data to online routine */ int phys_device; /* to which fru does this belong? */ void *hw; /* optional pointer to fw/hw data */ int (*phys_callback)(struct memory_block *); -- cgit v1.2.3-71-gd317 From fb7589a162162223e6bb6422dde3fb1ce07d9a78 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 21 Aug 2013 14:31:38 +0400 Subject: tun: Add ability to create tun device with given index Tun devices cannot be created with ifidex user wants, but it's required by checkpoint-restore project. Long time ago such ability was implemented for rtnl_ops-based interface for creating links (9c7dafbf net: Allow to create links with given ifindex), but the only API for creating and managing tuntap devices is ioctl-based and is evolving with adding new ones (cde8b15f tuntap: add ioctl to attach or detach a file form tuntap device). Following that trend, here's how a new ioctl that sets the ifindex for device, that _will_ be created by TUNSETIFF ioctl looks like. So those who want a tuntap device with the ifindex N, should open the tun device, call ioctl(fd, TUNSETIFINDEX, &N), then call TUNSETIFF. If the index N is busy, then the register_netdev will find this out and the ioctl would be failed with -EBUSY. If setifindex is not called, then it will be generated as before. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- drivers/net/tun.c | 21 ++++++++++++++++++++- include/uapi/linux/if_tun.h | 1 + 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 7ed13cc0dcb2..4b65fbcc490f 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -138,7 +138,10 @@ struct tun_file { struct fasync_struct *fasync; /* only used for fasnyc */ unsigned int flags; - u16 queue_index; + union { + u16 queue_index; + unsigned int ifindex; + }; struct list_head next; struct tun_struct *detached; }; @@ -1601,6 +1604,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) dev_net_set(dev, net); dev->rtnl_link_ops = &tun_link_ops; + dev->ifindex = tfile->ifindex; tun = netdev_priv(dev); tun->dev = dev; @@ -1817,6 +1821,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, kgid_t group; int sndbuf; int vnet_hdr_sz; + unsigned int ifindex; int ret; if (cmd == TUNSETIFF || cmd == TUNSETQUEUE || _IOC_TYPE(cmd) == 0x89) { @@ -1851,6 +1856,19 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, ret = -EFAULT; goto unlock; } + if (cmd == TUNSETIFINDEX) { + ret = -EPERM; + if (tun) + goto unlock; + + ret = -EFAULT; + if (copy_from_user(&ifindex, argp, sizeof(ifindex))) + goto unlock; + + ret = 0; + tfile->ifindex = ifindex; + goto unlock; + } ret = -EBADFD; if (!tun) @@ -2099,6 +2117,7 @@ static int tun_chr_open(struct inode *inode, struct file * file) rcu_assign_pointer(tfile->tun, NULL); tfile->net = get_net(current->nsproxy->net_ns); tfile->flags = 0; + tfile->ifindex = 0; rcu_assign_pointer(tfile->socket.wq, &tfile->wq); init_waitqueue_head(&tfile->wq.wait); diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h index 1870ee29bb37..c58d023c4822 100644 --- a/include/uapi/linux/if_tun.h +++ b/include/uapi/linux/if_tun.h @@ -56,6 +56,7 @@ #define TUNGETVNETHDRSZ _IOR('T', 215, int) #define TUNSETVNETHDRSZ _IOW('T', 216, int) #define TUNSETQUEUE _IOW('T', 217, int) +#define TUNSETIFINDEX _IOW('T', 218, unsigned int) /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 -- cgit v1.2.3-71-gd317 From 849c9b6f93cc4cb5eb59301b6380a7a81b43f414 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 21 Aug 2013 14:32:21 +0400 Subject: tun: Allow to skip filter on attach There's a small problem with sk-filters on tun devices. Consider an application doing this sequence of steps: fd = open("/dev/net/tun"); ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" }); ioctl(fd, TUNATTACHFILTER, &my_filter); ioctl(fd, TUNSETPERSIST, 1); close(fd); At that point the tun0 will remain in the system and will keep in mind that there should be a socket filter at address '&my_filter'. If after that we do fd = open("/dev/net/tun"); ioctl(fd, TUNSETIFF, { .ifr_name = "tun0" }); we most likely receive the -EFAULT error, since tun_attach() would try to connect the filter back. But (!) if we provide a filter at address &my_filter, then tun0 will be created and the "new" filter would be attached, but application may not know about that. This may create certain problems to anyone using tun-s, but it's critical problem for c/r -- if we meet a persistent tun device with a filter in mind, we will not be able to attach to it to dump its state (flags, owner, address, vnethdr size, etc.). The proposal is to allow to attach to tun device (with TUNSETIFF) w/o attaching the filter to the tun-file's socket. After this attach app may e.g clean the device by dropping the filter, it doesn't want to have one, or (in case of c/r) get information about the device with tun ioctls. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- drivers/net/tun.c | 12 +++++++----- include/uapi/linux/if_tun.h | 1 + 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index db43a2409733..6acbdbccebcd 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -501,7 +501,7 @@ static void tun_detach_all(struct net_device *dev) module_put(THIS_MODULE); } -static int tun_attach(struct tun_struct *tun, struct file *file) +static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filter) { struct tun_file *tfile = file->private_data; int err; @@ -526,7 +526,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file) err = 0; /* Re-attach the filter to presist device */ - if (tun->filter_attached == true) { + if (!skip_filter && (tun->filter_attached == true)) { err = sk_attach_filter(&tun->fprog, tfile->socket.sk); if (!err) goto out; @@ -1557,7 +1557,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) if (err < 0) return err; - err = tun_attach(tun, file); + err = tun_attach(tun, file, ifr->ifr_flags & IFF_NOFILTER); if (err < 0) return err; @@ -1631,7 +1631,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) dev->vlan_features = dev->features; INIT_LIST_HEAD(&tun->disabled); - err = tun_attach(tun, file); + err = tun_attach(tun, file, false); if (err < 0) goto err_free_dev; @@ -1795,7 +1795,7 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr) ret = security_tun_dev_attach_queue(tun->security); if (ret < 0) goto unlock; - ret = tun_attach(tun, file); + ret = tun_attach(tun, file, false); } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { tun = rtnl_dereference(tfile->tun); if (!tun || !(tun->flags & TUN_TAP_MQ) || tfile->detached) @@ -1883,6 +1883,8 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, if (tfile->detached) ifr.ifr_flags |= IFF_DETACH_QUEUE; + if (!tfile->socket.sk->sk_filter) + ifr.ifr_flags |= IFF_NOFILTER; if (copy_to_user(argp, &ifr, ifreq_len)) ret = -EFAULT; diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h index c58d023c4822..cc127b2b4c3d 100644 --- a/include/uapi/linux/if_tun.h +++ b/include/uapi/linux/if_tun.h @@ -71,6 +71,7 @@ #define IFF_DETACH_QUEUE 0x0400 /* read-only flag */ #define IFF_PERSIST 0x0800 +#define IFF_NOFILTER 0x1000 /* Socket options */ #define TUN_TX_TIMESTAMP 1 -- cgit v1.2.3-71-gd317 From 76975e9cb4a7c6fe39478a3dc4dd292a5c6c8c74 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Wed, 21 Aug 2013 14:32:39 +0400 Subject: tun: Get skfilter layout The only thing we may have from tun device is the fprog, whic contains the number of filter elements and a pointer to (user-space) memory where the elements are. The program itself may not be available if the device is persistent and detached. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- drivers/net/tun.c | 10 ++++++++++ include/uapi/linux/if_tun.h | 1 + 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 6acbdbccebcd..60a1e93e9d35 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2042,6 +2042,16 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, tun_detach_filter(tun, tun->numqueues); break; + case TUNGETFILTER: + ret = -EINVAL; + if ((tun->flags & TUN_TYPE_MASK) != TUN_TAP_DEV) + break; + ret = -EFAULT; + if (copy_to_user(argp, &tun->fprog, sizeof(tun->fprog))) + break; + ret = 0; + break; + default: ret = -EINVAL; break; diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h index cc127b2b4c3d..e9502dd1ee2c 100644 --- a/include/uapi/linux/if_tun.h +++ b/include/uapi/linux/if_tun.h @@ -57,6 +57,7 @@ #define TUNSETVNETHDRSZ _IOW('T', 216, int) #define TUNSETQUEUE _IOW('T', 217, int) #define TUNSETIFINDEX _IOW('T', 218, unsigned int) +#define TUNGETFILTER _IOR('T', 219, struct sock_fprog) /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 -- cgit v1.2.3-71-gd317 From 3e9b2bae8369661070622d05570cbcdfa01770e6 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 21 Aug 2013 13:47:50 -0700 Subject: sysfs: add sysfs_create/remove_groups() These functions are being open-coded in 3 different places in the driver core, and other driver subsystems will want to start doing this as well, so move it to the sysfs core to keep it all in one place, where we know it is written properly. Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 23 ++-------------------- drivers/base/core.c | 22 ++------------------- drivers/base/driver.c | 22 ++------------------- fs/sysfs/group.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysfs.h | 4 ++++ 5 files changed, 64 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 5ee5d3c1d74b..f099af0b41af 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -884,32 +884,13 @@ static void bus_remove_attrs(struct bus_type *bus) static int bus_add_groups(struct bus_type *bus, const struct attribute_group **groups) { - int error = 0; - int i; - - if (groups) { - for (i = 0; groups[i]; i++) { - error = sysfs_create_group(&bus->p->subsys.kobj, - groups[i]); - if (error) { - while (--i >= 0) - sysfs_remove_group(&bus->p->subsys.kobj, - groups[i]); - break; - } - } - } - return error; + return sysfs_create_groups(&bus->p->subsys.kobj, groups); } static void bus_remove_groups(struct bus_type *bus, const struct attribute_group **groups) { - int i; - - if (groups) - for (i = 0; groups[i]; i++) - sysfs_remove_group(&bus->p->subsys.kobj, groups[i]); + sysfs_remove_groups(&bus->p->subsys.kobj, groups); } static void klist_devices_get(struct klist_node *n) diff --git a/drivers/base/core.c b/drivers/base/core.c index af646afa5b7e..d743dcee1e77 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -493,31 +493,13 @@ static void device_remove_bin_attributes(struct device *dev, int device_add_groups(struct device *dev, const struct attribute_group **groups) { - int error = 0; - int i; - - if (groups) { - for (i = 0; groups[i]; i++) { - error = sysfs_create_group(&dev->kobj, groups[i]); - if (error) { - while (--i >= 0) - sysfs_remove_group(&dev->kobj, - groups[i]); - break; - } - } - } - return error; + return sysfs_create_groups(&dev->kobj, groups); } void device_remove_groups(struct device *dev, const struct attribute_group **groups) { - int i; - - if (groups) - for (i = 0; groups[i]; i++) - sysfs_remove_group(&dev->kobj, groups[i]); + sysfs_remove_groups(&dev->kobj, groups); } static int device_add_attrs(struct device *dev) diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 89db726ebb98..c7efccb6f3bb 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -126,31 +126,13 @@ EXPORT_SYMBOL_GPL(driver_remove_file); int driver_add_groups(struct device_driver *drv, const struct attribute_group **groups) { - int error = 0; - int i; - - if (groups) { - for (i = 0; groups[i]; i++) { - error = sysfs_create_group(&drv->p->kobj, groups[i]); - if (error) { - while (--i >= 0) - sysfs_remove_group(&drv->p->kobj, - groups[i]); - break; - } - } - } - return error; + return sysfs_create_groups(&drv->p->kobj, groups); } void driver_remove_groups(struct device_driver *drv, const struct attribute_group **groups) { - int i; - - if (groups) - for (i = 0; groups[i]; i++) - sysfs_remove_group(&drv->p->kobj, groups[i]); + sysfs_remove_groups(&drv->p->kobj, groups); } /** diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 09a1a25cd145..68baf8501552 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -130,6 +130,40 @@ int sysfs_create_group(struct kobject *kobj, return internal_create_group(kobj, 0, grp); } +/** + * sysfs_create_groups - given a directory kobject, create a bunch of attribute groups + * @kobj: The kobject to create the group on + * @groups: The attribute groups to create, NULL terminated + * + * This function creates a bunch of attribute groups. If an error occurs when + * creating a group, all previously created groups will be removed, unwinding + * everything back to the original state when this function was called. + * It will explicitly warn and error if any of the attribute files being + * created already exist. + * + * Returns 0 on success or error code from sysfs_create_groups on error. + */ +int sysfs_create_groups(struct kobject *kobj, + const struct attribute_group **groups) +{ + int error = 0; + int i; + + if (!groups) + return 0; + + for (i = 0; groups[i]; i++) { + error = sysfs_create_group(kobj, groups[i]); + if (error) { + while (--i >= 0) + sysfs_remove_group(kobj, groups[i]); + break; + } + } + return error; +} +EXPORT_SYMBOL_GPL(sysfs_create_groups); + /** * sysfs_update_group - given a directory kobject, update an attribute group * @kobj: The kobject to update the group on @@ -178,6 +212,26 @@ void sysfs_remove_group(struct kobject * kobj, sysfs_put(sd); } +/** + * sysfs_remove_groups - remove a list of groups + * + * kobj: The kobject for the groups to be removed from + * groups: NULL terminated list of groups to be removed + * + * If groups is not NULL, the all groups will be removed from the kobject + */ +void sysfs_remove_groups(struct kobject *kobj, + const struct attribute_group **groups) +{ + int i; + + if (!groups) + return; + for (i = 0; groups[i]; i++) + sysfs_remove_group(kobj, groups[i]); +} +EXPORT_SYMBOL_GPL(sysfs_remove_groups); + /** * sysfs_merge_group - merge files into a pre-existing attribute group. * @kobj: The kobject containing the group. diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 0f04958623d7..4dbb9d31daa8 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -215,10 +215,14 @@ void sysfs_delete_link(struct kobject *dir, struct kobject *targ, int __must_check sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp); +int __must_check sysfs_create_groups(struct kobject *kobj, + const struct attribute_group **groups); int sysfs_update_group(struct kobject *kobj, const struct attribute_group *grp); void sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp); +void sysfs_remove_groups(struct kobject *kobj, + const struct attribute_group **groups); int sysfs_add_file_to_group(struct kobject *kobj, const struct attribute *attr, const char *group); void sysfs_remove_file_from_group(struct kobject *kobj, -- cgit v1.2.3-71-gd317 From 5da5c9c899dc456d6eb43aae3e8d4e853b7f5306 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 21 Aug 2013 17:47:05 -0700 Subject: sysfs: fix up minor coding style issues in sysfs.h As long as we are cleaning up sysfs coding style issues, don't forget the main sysfs.h file, so fix up the space issues there as well. Signed-off-by: Greg Kroah-Hartman --- include/linux/sysfs.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 4dbb9d31daa8..a864b8d918ee 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -51,9 +51,9 @@ do { \ static struct lock_class_key __key; \ \ (attr)->key = &__key; \ -} while(0) +} while (0) #else -#define sysfs_attr_init(attr) do {} while(0) +#define sysfs_attr_init(attr) do {} while (0) #endif struct attribute_group { @@ -69,7 +69,7 @@ struct attribute_group { * for examples.. */ -#define __ATTR(_name,_mode,_show,_store) { \ +#define __ATTR(_name, _mode, _show, _store) { \ .attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ @@ -119,7 +119,7 @@ struct bin_attribute { void *private; ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t); - ssize_t (*write)(struct file *,struct kobject *, struct bin_attribute *, + ssize_t (*write)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t); int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr, struct vm_area_struct *vma); @@ -168,8 +168,8 @@ struct bin_attribute bin_attr_##_name = __BIN_ATTR_RO(_name, _size) struct bin_attribute bin_attr_##_name = __BIN_ATTR_RW(_name, _size) struct sysfs_ops { - ssize_t (*show)(struct kobject *, struct attribute *,char *); - ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); + ssize_t (*show)(struct kobject *, struct attribute *, char *); + ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t); const void *(*namespace)(struct kobject *, const struct attribute *); }; -- cgit v1.2.3-71-gd317 From 35c7ab421a13f8327e3fd627c6ebafb1c13b2e55 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 10 Aug 2013 14:51:11 +0200 Subject: drm/i915: reserve I915_CACHING_DISPLAY and document cache modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve the catch-22 of igt needing a stable number and patches first needing testcases by reserving the interface number up-front. v2: Improve the spelling a bit. v3: More spelling fail spotted by Chris. Requested-by: Chris Wilson Cc: Chris Wilson Cc: Ville Syrjälä Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- include/uapi/drm/i915_drm.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'include') diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index a1a7b6bd60d8..0bb3e5524382 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -768,8 +768,32 @@ struct drm_i915_gem_busy { __u32 busy; }; +/** + * I915_CACHING_NONE + * + * GPU access is not coherent with cpu caches. Default for machines without an + * LLC. + */ #define I915_CACHING_NONE 0 +/** + * I915_CACHING_CACHED + * + * GPU access is coherent with cpu caches and furthermore the data is cached in + * last-level caches shared between cpu cores and the gpu GT. Default on + * machines with HAS_LLC. + */ #define I915_CACHING_CACHED 1 +/** + * I915_CACHING_DISPLAY + * + * Special GPU caching mode which is coherent with the scanout engines. + * Transparently falls back to I915_CACHING_NONE on platforms where no special + * cache mode (like write-through or gfdt flushing) is available. The kernel + * automatically sets this mode when using a buffer as a scanout target. + * Userspace can manually set this mode to avoid a costly stall and clflush in + * the hotpath of drawing the first frame. + */ +#define I915_CACHING_DISPLAY 2 struct drm_i915_gem_caching { /** -- cgit v1.2.3-71-gd317 From 651d794fae9b79237aae1c97f8a9d9f3817bd31d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 8 Aug 2013 14:41:10 +0100 Subject: drm/i915: Use Write-Through cacheing for the display plane on Iris MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Haswell GT3e has the unique feature of supporting Write-Through cacheing of objects within the eLLC/LLC. The purpose of this is to enable the display plane to remain coherent whilst objects lie resident in the eLLC/LLC - so that we, in theory, get the best of both worlds, perfect display and fast access. However, we still need to be careful as the CPU does not see the WT when accessing the cache. In particular, this means that we need to flush the cache lines after writing to an object through the CPU, and on transitioning from a cached state to WT. v2: Actually do the clflush on transition to WT, nagging by Ville. v3: Flush the CPU cache after writes into WT objects. v4: Rease onto LLC updates and report WT as "uncached" for get_cache_level_ioctl to remain symmetric with set_cache_level_ioctl. Signed-off-by: Chris Wilson Cc: Ville Syrjälä Cc: Kenneth Graunke Reviewed-by: Ville Syrjälä Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 3 +++ drivers/gpu/drm/i915/i915_drv.h | 4 +++- drivers/gpu/drm/i915/i915_gem.c | 14 ++++++++++++-- drivers/gpu/drm/i915/i915_gem_gtt.c | 11 ++++++++++- include/uapi/drm/i915_drm.h | 1 + 5 files changed, 29 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index ce098c3ccc00..f4231185ec7d 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -976,6 +976,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_LLC: value = HAS_LLC(dev); break; + case I915_PARAM_HAS_WT: + value = HAS_WT(dev); + break; case I915_PARAM_HAS_ALIASING_PPGTT: value = dev_priv->mm.aliasing_ppgtt ? 1 : 0; break; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 1af59d72ddc7..6d07467d0e7e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -454,6 +454,7 @@ enum i915_cache_level { caches, eg sampler/render caches, and the large Last-Level-Cache. LLC is coherent with the CPU, but L3 is only visible to the GPU. */ + I915_CACHE_WT, /* hsw:gt3e WriteThrough for scanouts */ }; typedef uint32_t gen6_gtt_pte_t; @@ -1385,7 +1386,7 @@ struct drm_i915_gem_object { unsigned int pending_fenced_gpu_access:1; unsigned int fenced_gpu_access:1; - unsigned int cache_level:2; + unsigned int cache_level:3; unsigned int has_aliasing_ppgtt_mapping:1; unsigned int has_global_gtt_mapping:1; @@ -1530,6 +1531,7 @@ struct drm_i915_file_private { #define HAS_BLT(dev) (INTEL_INFO(dev)->has_blt_ring) #define HAS_VEBOX(dev) (INTEL_INFO(dev)->has_vebox_ring) #define HAS_LLC(dev) (INTEL_INFO(dev)->has_llc) +#define HAS_WT(dev) (IS_HASWELL(dev) && to_i915(dev)->ellc_size) #define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws) #define HAS_HW_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 6) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 474748ffa7b6..4064fdf15abe 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3471,7 +3471,16 @@ int i915_gem_get_caching_ioctl(struct drm_device *dev, void *data, goto unlock; } - args->caching = obj->cache_level != I915_CACHE_NONE; + switch (obj->cache_level) { + case I915_CACHE_LLC: + case I915_CACHE_L3_LLC: + args->caching = I915_CACHING_CACHED; + break; + + default: + args->caching = I915_CACHING_NONE; + break; + } drm_gem_object_unreference(&obj->base); unlock: @@ -3565,7 +3574,8 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, * of uncaching, which would allow us to flush all the LLC-cached data * with that bit in the PTE to main memory with just one PIPE_CONTROL. */ - ret = i915_gem_object_set_cache_level(obj, I915_CACHE_NONE); + ret = i915_gem_object_set_cache_level(obj, + HAS_WT(obj->base.dev) ? I915_CACHE_WT : I915_CACHE_NONE); if (ret) goto err_unpin_display; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index c9420c280cf0..212f6d8c35ec 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -55,6 +55,7 @@ #define HSW_WB_LLC_AGE3 HSW_CACHEABILITY_CONTROL(0x2) #define HSW_WB_LLC_AGE0 HSW_CACHEABILITY_CONTROL(0x3) #define HSW_WB_ELLC_LLC_AGE0 HSW_CACHEABILITY_CONTROL(0xb) +#define HSW_WT_ELLC_LLC_AGE0 HSW_CACHEABILITY_CONTROL(0x6) static gen6_gtt_pte_t snb_pte_encode(dma_addr_t addr, enum i915_cache_level level) @@ -138,8 +139,16 @@ static gen6_gtt_pte_t iris_pte_encode(dma_addr_t addr, gen6_gtt_pte_t pte = GEN6_PTE_VALID; pte |= HSW_PTE_ADDR_ENCODE(addr); - if (level != I915_CACHE_NONE) + switch (level) { + case I915_CACHE_NONE: + break; + case I915_CACHE_WT: + pte |= HSW_WT_ELLC_LLC_AGE0; + break; + default: pte |= HSW_WB_ELLC_LLC_AGE0; + break; + } return pte; } diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 0bb3e5524382..55bb5729bd78 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -334,6 +334,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_PINNED_BATCHES 24 #define I915_PARAM_HAS_EXEC_NO_RELOC 25 #define I915_PARAM_HAS_EXEC_HANDLE_LUT 26 +#define I915_PARAM_HAS_WT 27 typedef struct drm_i915_getparam { int param; -- cgit v1.2.3-71-gd317 From f477b7fb13df2b843997559ff34e87d054ba6538 Mon Sep 17 00:00:00 2001 From: wangyuhang Date: Sun, 11 Aug 2013 18:15:17 +0800 Subject: spi: DUAL and QUAD support fix the previous patch some mistake below: 1. DT in slave node, use "spi-tx-nbits = <1/2/4>" in place of using "spi-tx-dual, spi-tx-quad" directly, same to rx. So correct the previous way to get the property in @of_register_spi_devices(). 2. Change the value of transfer bit macro(SPI_NBITS_SINGLE, SPI_NBITS_DUAL SPI_NBITS_QUAD) to 0x01, 0x02 and 0x04 to match the actual wires. 3. Add the following check (1)keep the tx_nbits and rx_nbits in spi_transfer is not beyond the single, dual and quad. (2)keep tx_nbits and rx_nbits are contained by @spi_device->mode example: if @spi_device->mode = DUAL, then tx/rx_nbits can not be set to QUAD(SPI_NBITS_QUAD) (3)if "@spi_device->mode & SPI_3WIRE", then tx/rx_nbits should be in single(SPI_NBITS_SINGLE) Signed-off-by: wangyuhang Signed-off-by: Mark Brown --- drivers/spi/spi.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/spi/spi.h | 22 ++++++++++-- 2 files changed, 116 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 2a20c32c8277..39d38756e984 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -869,6 +869,51 @@ static void of_register_spi_devices(struct spi_master *master) if (of_find_property(nc, "spi-3wire", NULL)) spi->mode |= SPI_3WIRE; + /* Device DUAL/QUAD mode */ + prop = of_get_property(nc, "spi-tx-nbits", &len); + if (!prop || len < sizeof(*prop)) { + dev_err(&master->dev, "%s has no 'spi-tx-nbits' property\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + switch (be32_to_cpup(prop)) { + case SPI_NBITS_SINGLE: + break; + case SPI_NBITS_DUAL: + spi->mode |= SPI_TX_DUAL; + break; + case SPI_NBITS_QUAD: + spi->mode |= SPI_TX_QUAD; + break; + default: + dev_err(&master->dev, "spi-tx-nbits value is not supported\n"); + spi_dev_put(spi); + continue; + } + + prop = of_get_property(nc, "spi-rx-nbits", &len); + if (!prop || len < sizeof(*prop)) { + dev_err(&master->dev, "%s has no 'spi-rx-nbits' property\n", + nc->full_name); + spi_dev_put(spi); + continue; + } + switch (be32_to_cpup(prop)) { + case SPI_NBITS_SINGLE: + break; + case SPI_NBITS_DUAL: + spi->mode |= SPI_RX_DUAL; + break; + case SPI_NBITS_QUAD: + spi->mode |= SPI_RX_QUAD; + break; + default: + dev_err(&master->dev, "spi-rx-nbits value is not supported\n"); + spi_dev_put(spi); + continue; + } + /* Device speed */ prop = of_get_property(nc, "spi-max-frequency", &len); if (!prop || len < sizeof(*prop)) { @@ -1316,6 +1361,19 @@ int spi_setup(struct spi_device *spi) unsigned bad_bits; int status = 0; + /* check mode to prevent that DUAL and QUAD set at the same time + */ + if (((spi->mode & SPI_TX_DUAL) && (spi->mode & SPI_TX_QUAD)) || + ((spi->mode & SPI_RX_DUAL) && (spi->mode & SPI_RX_QUAD))) { + dev_err(&spi->dev, + "setup: can not select dual and quad at the same time\n"); + return -EINVAL; + } + /* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden + */ + if ((spi->mode & SPI_3WIRE) && (spi->mode & + (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD))) + return -EINVAL; /* help drivers fail *cleanly* when they need options * that aren't supported with their current master */ @@ -1378,6 +1436,8 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) /** * Set transfer bits_per_word and max speed as spi device default if * it is not set for this transfer. + * Set transfer tx_nbits and rx_nbits as single transfer default + * (SPI_NBITS_SINGLE) if it is not set for this transfer. */ list_for_each_entry(xfer, &message->transfers, transfer_list) { if (!xfer->bits_per_word) @@ -1403,6 +1463,42 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) return -EINVAL; if (xfer->speed_hz && master->max_speed_hz && xfer->speed_hz > master->max_speed_hz) + + if (xfer->tx_buf && !xfer->tx_nbits) + xfer->tx_nbits = SPI_NBITS_SINGLE; + if (xfer->rx_buf && !xfer->rx_nbits) + xfer->rx_nbits = SPI_NBITS_SINGLE; + /* check transfer tx/rx_nbits: + * 1. keep the value is not out of single, dual and quad + * 2. keep tx/rx_nbits is contained by mode in spi_device + * 3. if SPI_3WIRE, tx/rx_nbits should be in single + */ + if (xfer->tx_nbits != SPI_NBITS_SINGLE && + xfer->tx_nbits != SPI_NBITS_DUAL && + xfer->tx_nbits != SPI_NBITS_QUAD) + return -EINVAL; + if ((xfer->tx_nbits == SPI_NBITS_DUAL) && + !(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD))) + return -EINVAL; + if ((xfer->tx_nbits == SPI_NBITS_QUAD) && + !(spi->mode & SPI_TX_QUAD)) + return -EINVAL; + if ((spi->mode & SPI_3WIRE) && + (xfer->tx_nbits != SPI_NBITS_SINGLE)) + return -EINVAL; + /* check transfer rx_nbits */ + if (xfer->rx_nbits != SPI_NBITS_SINGLE && + xfer->rx_nbits != SPI_NBITS_DUAL && + xfer->rx_nbits != SPI_NBITS_QUAD) + return -EINVAL; + if ((xfer->rx_nbits == SPI_NBITS_DUAL) && + !(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD))) + return -EINVAL; + if ((xfer->rx_nbits == SPI_NBITS_QUAD) && + !(spi->mode & SPI_RX_QUAD)) + return -EINVAL; + if ((spi->mode & SPI_3WIRE) && + (xfer->rx_nbits != SPI_NBITS_SINGLE)) return -EINVAL; } diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index cdf668156154..ccd7840c4507 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -74,7 +74,7 @@ struct spi_device { struct spi_master *master; u32 max_speed_hz; u8 chip_select; - u8 mode; + u16 mode; #define SPI_CPHA 0x01 /* clock phase */ #define SPI_CPOL 0x02 /* clock polarity */ #define SPI_MODE_0 (0|0) /* (original MicroWire) */ @@ -87,6 +87,10 @@ struct spi_device { #define SPI_LOOP 0x20 /* loopback mode */ #define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */ #define SPI_READY 0x80 /* slave pulls low to pause */ +#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */ +#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */ +#define SPI_RX_DUAL 0x400 /* receive with 2 wires */ +#define SPI_RX_QUAD 0x800 /* receive with 4 wires */ u8 bits_per_word; int irq; void *controller_state; @@ -454,6 +458,10 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum); * @rx_buf: data to be read (dma-safe memory), or NULL * @tx_dma: DMA address of tx_buf, if @spi_message.is_dma_mapped * @rx_dma: DMA address of rx_buf, if @spi_message.is_dma_mapped + * @tx_nbits: number of bits used for writting. If 0 the default + * (SPI_NBITS_SINGLE) is used. + * @rx_nbits: number of bits used for reading. If 0 the default + * (SPI_NBITS_SINGLE) is used. * @len: size of rx and tx buffers (in bytes) * @speed_hz: Select a speed other than the device default for this * transfer. If 0 the default (from @spi_device) is used. @@ -508,6 +516,11 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum); * by the results of previous messages and where the whole transaction * ends when the chipselect goes intactive. * + * When SPI can transfer in 1x,2x or 4x. It can get this tranfer information + * from device through @tx_nbits and @rx_nbits. In Bi-direction, these + * two should both be set. User can set transfer mode with SPI_NBITS_SINGLE(1x) + * SPI_NBITS_DUAL(2x) and SPI_NBITS_QUAD(4x) to support these three transfer. + * * The code that submits an spi_message (and its spi_transfers) * to the lower layers is responsible for managing its memory. * Zero-initialize every field you don't set up explicitly, to @@ -528,6 +541,11 @@ struct spi_transfer { dma_addr_t rx_dma; unsigned cs_change:1; + u8 tx_nbits; + u8 rx_nbits; +#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */ +#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */ +#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */ u8 bits_per_word; u16 delay_usecs; u32 speed_hz; @@ -875,7 +893,7 @@ struct spi_board_info { /* mode becomes spi_device.mode, and is essential for chips * where the default of SPI_CS_HIGH = 0 is wrong. */ - u8 mode; + u16 mode; /* ... may need additional spi_device chip config data here. * avoid stuff protocol drivers can set; but include stuff -- cgit v1.2.3-71-gd317 From 3300a8fd48976e7126cf078de95f52d59e413bb0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 5 Jul 2013 07:16:02 -0300 Subject: [media] mt9v032: Use the common clock framework Configure the device external clock using the common clock framework instead of a board code callback function. Signed-off-by: Laurent Pinchart Acked-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/mt9v032.c | 17 +++++++++++------ include/media/mt9v032.h | 4 ---- 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 60c6f6739560..2c50effaa334 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -12,6 +12,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -135,6 +136,8 @@ struct mt9v032 { struct mutex power_lock; int power_count; + struct clk *clk; + struct mt9v032_platform_data *pdata; u32 sysclk; @@ -219,10 +222,9 @@ static int mt9v032_power_on(struct mt9v032 *mt9v032) struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); int ret; - if (mt9v032->pdata->set_clock) { - mt9v032->pdata->set_clock(&mt9v032->subdev, mt9v032->sysclk); - udelay(1); - } + clk_set_rate(mt9v032->clk, mt9v032->sysclk); + clk_prepare_enable(mt9v032->clk); + udelay(1); /* Reset the chip and stop data read out */ ret = mt9v032_write(client, MT9V032_RESET, 1); @@ -238,8 +240,7 @@ static int mt9v032_power_on(struct mt9v032 *mt9v032) static void mt9v032_power_off(struct mt9v032 *mt9v032) { - if (mt9v032->pdata->set_clock) - mt9v032->pdata->set_clock(&mt9v032->subdev, 0); + clk_disable_unprepare(mt9v032->clk); } static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) @@ -748,6 +749,10 @@ static int mt9v032_probe(struct i2c_client *client, if (!mt9v032) return -ENOMEM; + mt9v032->clk = devm_clk_get(&client->dev, NULL); + if (IS_ERR(mt9v032->clk)) + return PTR_ERR(mt9v032->clk); + mutex_init(&mt9v032->power_lock); mt9v032->pdata = pdata; diff --git a/include/media/mt9v032.h b/include/media/mt9v032.h index 78fd39eac219..12175a63c5b2 100644 --- a/include/media/mt9v032.h +++ b/include/media/mt9v032.h @@ -1,13 +1,9 @@ #ifndef _MEDIA_MT9V032_H #define _MEDIA_MT9V032_H -struct v4l2_subdev; - struct mt9v032_platform_data { unsigned int clk_pol:1; - void (*set_clock)(struct v4l2_subdev *subdev, unsigned int rate); - const s64 *link_freqs; s64 link_def_freq; }; -- cgit v1.2.3-71-gd317 From a354177f058541b7212230feb2c0da7c464e9b9d Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sat, 10 Aug 2013 14:49:48 -0300 Subject: [media] smiapp: Call the clock "ext_clk" As the clock framework makes it possible to assign a device specific name to the clocks, remove the ability to use a named clock in the driver. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-core.c | 9 +++------ include/media/smiapp.h | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 7de9892fb6c9..ae66d91bf713 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -2363,11 +2363,9 @@ static int smiapp_registered(struct v4l2_subdev *subdev) } if (!sensor->platform_data->set_xclk) { - sensor->ext_clk = devm_clk_get(&client->dev, - sensor->platform_data->ext_clk_name); + sensor->ext_clk = devm_clk_get(&client->dev, "ext_clk"); if (IS_ERR(sensor->ext_clk)) { - dev_err(&client->dev, "could not get clock %s\n", - sensor->platform_data->ext_clk_name); + dev_err(&client->dev, "could not get clock\n"); return -ENODEV; } @@ -2375,8 +2373,7 @@ static int smiapp_registered(struct v4l2_subdev *subdev) sensor->platform_data->ext_clk); if (rval < 0) { dev_err(&client->dev, - "unable to set clock %s freq to %u\n", - sensor->platform_data->ext_clk_name, + "unable to set clock freq to %u\n", sensor->platform_data->ext_clk); return -ENODEV; } diff --git a/include/media/smiapp.h b/include/media/smiapp.h index 07f96a89e189..0b8f124a630c 100644 --- a/include/media/smiapp.h +++ b/include/media/smiapp.h @@ -77,7 +77,6 @@ struct smiapp_platform_data { struct smiapp_flash_strobe_parms *strobe_setup; int (*set_xclk)(struct v4l2_subdev *sd, int hz); - char *ext_clk_name; int xshutdown; /* gpio or SMIAPP_NO_XSHUTDOWN */ }; -- cgit v1.2.3-71-gd317 From dc76a1adfa12ad11957bdeec565dbccf37205d04 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 13 Aug 2013 09:59:00 -0500 Subject: phy: micrel: Add definitions for common Micrel PHY registers Add defines for common Micrel PHY setups so that other platforms can use them. Update imx61 and sama5 hardware to use the micrel_phy.h PHY defines. Also add support for the KSZ9021RLRN PHY. Signed-off-by: Dinh Nguyen Acked-by: Nicolas Ferre Acked-by: David S. Miller CC: Andrew Victor CC: Jean-Christophe Plagniol-Villard Cc: netdev@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Shawn Guo --- arch/arm/mach-at91/board-dt-sama5.c | 17 ++++++----------- arch/arm/mach-imx/mach-imx6q.c | 13 ++++++++----- include/linux/micrel_phy.h | 6 ++++++ 3 files changed, 20 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-at91/board-dt-sama5.c b/arch/arm/mach-at91/board-dt-sama5.c index ad95f6a23a28..bf00d15d954d 100644 --- a/arch/arm/mach-at91/board-dt-sama5.c +++ b/arch/arm/mach-at91/board-dt-sama5.c @@ -42,20 +42,15 @@ static int ksz9021rn_phy_fixup(struct phy_device *phy) { int value; -#define GMII_RCCPSR 260 -#define GMII_RRDPSR 261 -#define GMII_ERCR 11 -#define GMII_ERDWR 12 - /* Set delay values */ - value = GMII_RCCPSR | 0x8000; - phy_write(phy, GMII_ERCR, value); + value = MICREL_KSZ9021_RGMII_CLK_CTRL_PAD_SCEW | 0x8000; + phy_write(phy, MICREL_KSZ9021_EXTREG_CTRL, value); value = 0xF2F4; - phy_write(phy, GMII_ERDWR, value); - value = GMII_RRDPSR | 0x8000; - phy_write(phy, GMII_ERCR, value); + phy_write(phy, MICREL_KSZ9021_EXTREG_DATA_WRITE, value); + value = MICREL_KSZ9021_RGMII_RX_DATA_PAD_SCEW | 0x8000; + phy_write(phy, MICREL_KSZ9021_EXTREG_CTRL, value); value = 0x2222; - phy_write(phy, GMII_ERDWR, value); + phy_write(phy, MICREL_KSZ9021_EXTREG_DATA_WRITE, value); return 0; } diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index c730af673085..1e093abe7b95 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -103,13 +103,16 @@ static int ksz9021rn_phy_fixup(struct phy_device *phydev) { if (IS_BUILTIN(CONFIG_PHYLIB)) { /* min rx data delay */ - phy_write(phydev, 0x0b, 0x8105); - phy_write(phydev, 0x0c, 0x0000); + phy_write(phydev, MICREL_KSZ9021_EXTREG_CTRL, + 0x8000 | MICREL_KSZ9021_RGMII_RX_DATA_PAD_SCEW); + phy_write(phydev, MICREL_KSZ9021_EXTREG_DATA_WRITE, 0x0000); /* max rx/tx clock delay, min rx/tx control delay */ - phy_write(phydev, 0x0b, 0x8104); - phy_write(phydev, 0x0c, 0xf0f0); - phy_write(phydev, 0x0b, 0x104); + phy_write(phydev, MICREL_KSZ9021_EXTREG_CTRL, + 0x8000 | MICREL_KSZ9021_RGMII_CLK_CTRL_PAD_SCEW); + phy_write(phydev, MICREL_KSZ9021_EXTREG_DATA_WRITE, 0xf0f0); + phy_write(phydev, MICREL_KSZ9021_EXTREG_CTRL, + MICREL_KSZ9021_RGMII_CLK_CTRL_PAD_SCEW); } return 0; diff --git a/include/linux/micrel_phy.h b/include/linux/micrel_phy.h index 8752dbbc6135..ad05ce60c1c9 100644 --- a/include/linux/micrel_phy.h +++ b/include/linux/micrel_phy.h @@ -17,6 +17,7 @@ #define PHY_ID_KSZ8873MLL 0x000e7237 #define PHY_ID_KSZ9021 0x00221610 +#define PHY_ID_KSZ9021RLRN 0x00221611 #define PHY_ID_KS8737 0x00221720 #define PHY_ID_KSZ8021 0x00221555 #define PHY_ID_KSZ8031 0x00221556 @@ -35,4 +36,9 @@ /* struct phy_device dev_flags definitions */ #define MICREL_PHY_50MHZ_CLK 0x00000001 +#define MICREL_KSZ9021_EXTREG_CTRL 0xB +#define MICREL_KSZ9021_EXTREG_DATA_WRITE 0xC +#define MICREL_KSZ9021_RGMII_CLK_CTRL_PAD_SCEW 0x104 +#define MICREL_KSZ9021_RGMII_RX_DATA_PAD_SCEW 0x105 + #endif /* _MICREL_PHY_H */ -- cgit v1.2.3-71-gd317 From a58674ff8383f5b8f6a77f03c48f6a47840b9325 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 22 Aug 2013 11:24:44 +0800 Subject: PCI: Simplify pcie_bus_configure_settings() interface Based on a patch by Jon Mason (see URL below). All users of pcie_bus_configure_settings() pass arguments of the form "bus, bus->self->pcie_mpss". The "mpss" argument is redundant since we can easily look it up internally. In addition, all callers check "bus->self" for NULL, which we can also do internally. This patch simplifies the interface and the callers. No functional change. Reference: http://lkml.kernel.org/r/1317048850-30728-2-git-send-email-mason@myri.com Signed-off-by: Bjorn Helgaas --- arch/powerpc/kernel/pci-common.c | 8 ++------ arch/tile/kernel/pci_gx.c | 9 ++------- arch/x86/pci/acpi.c | 9 ++------- drivers/pci/hotplug/pcihp_slot.c | 5 ++--- drivers/pci/probe.c | 7 +++++-- include/linux/pci.h | 2 +- 6 files changed, 14 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index f46914a0f33e..d35ec34de1b4 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1672,12 +1672,8 @@ void pcibios_scan_phb(struct pci_controller *hose) /* Configure PCI Express settings */ if (bus && !pci_has_flag(PCI_PROBE_ONLY)) { struct pci_bus *child; - list_for_each_entry(child, &bus->children, node) { - struct pci_dev *self = child->self; - if (!self) - continue; - pcie_bus_configure_settings(child, self->pcie_mpss); - } + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); } } diff --git a/arch/tile/kernel/pci_gx.c b/arch/tile/kernel/pci_gx.c index 11425633b2d7..6640e7bbeaa2 100644 --- a/arch/tile/kernel/pci_gx.c +++ b/arch/tile/kernel/pci_gx.c @@ -508,13 +508,8 @@ static void fixup_read_and_payload_sizes(struct pci_controller *controller) rc_dev_cap.word); /* Configure PCI Express MPS setting. */ - list_for_each_entry(child, &root_bus->children, node) { - struct pci_dev *self = child->self; - if (!self) - continue; - - pcie_bus_configure_settings(child, self->pcie_mpss); - } + list_for_each_entry(child, &root_bus->children, node) + pcie_bus_configure_settings(child); /* * Set the mac_config register in trio based on the MPS/MRS of the link. diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index d641897a1f4e..b30e937689d6 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -568,13 +568,8 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) */ if (bus) { struct pci_bus *child; - list_for_each_entry(child, &bus->children, node) { - struct pci_dev *self = child->self; - if (!self) - continue; - - pcie_bus_configure_settings(child, self->pcie_mpss); - } + list_for_each_entry(child, &bus->children, node) + pcie_bus_configure_settings(child); } if (bus && node != -1) { diff --git a/drivers/pci/hotplug/pcihp_slot.c b/drivers/pci/hotplug/pcihp_slot.c index fec2d5b75440..16f920352317 100644 --- a/drivers/pci/hotplug/pcihp_slot.c +++ b/drivers/pci/hotplug/pcihp_slot.c @@ -160,9 +160,8 @@ void pci_configure_slot(struct pci_dev *dev) (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI))) return; - if (dev->bus && dev->bus->self) - pcie_bus_configure_settings(dev->bus, - dev->bus->self->pcie_mpss); + if (dev->bus) + pcie_bus_configure_settings(dev->bus); memset(&hpp, 0, sizeof(hpp)); ret = pci_get_hp_params(dev, &hpp); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9a334301a8f4..ecae7f290647 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1607,10 +1607,13 @@ static int pcie_bus_configure_set(struct pci_dev *dev, void *data) * parents then children fashion. If this changes, then this code will not * work as designed. */ -void pcie_bus_configure_settings(struct pci_bus *bus, u8 mpss) +void pcie_bus_configure_settings(struct pci_bus *bus) { u8 smpss; + if (!bus->self) + return; + if (!pci_is_pcie(bus->self)) return; @@ -1625,7 +1628,7 @@ void pcie_bus_configure_settings(struct pci_bus *bus, u8 mpss) smpss = 0; if (pcie_bus_config == PCIE_BUS_SAFE) { - smpss = mpss; + smpss = bus->self->pcie_mpss; pcie_find_smpss(bus->self, &smpss); pci_walk_bus(bus, pcie_find_smpss, &smpss); diff --git a/include/linux/pci.h b/include/linux/pci.h index 0fd1f1582fa1..57062b7a20ba 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -675,7 +675,7 @@ struct pci_driver { /* these external functions are only available when PCI support is enabled */ #ifdef CONFIG_PCI -void pcie_bus_configure_settings(struct pci_bus *bus, u8 smpss); +void pcie_bus_configure_settings(struct pci_bus *bus); enum pcie_bus_config_types { PCIE_BUS_TUNE_OFF, -- cgit v1.2.3-71-gd317 From 3e1026b3fa2f61d33ce6a9e42a22398cc4ab8e58 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 22 Aug 2013 10:25:34 -0700 Subject: sysfs.h: remove attr_name() macro Gotta love a macro that doesn't reduce the typing you have to do. Also, only the driver core, and one network driver uses this. The driver core functions will be going away soon, and I'll convert the network driver soon to not need this as well, so delete it for now before anyone else gets some bright ideas and wants to use it. Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 12 ++++++------ drivers/base/class.c | 4 ++-- drivers/base/core.c | 8 ++++---- drivers/net/ethernet/sun/niu.c | 2 +- include/linux/sysfs.h | 2 -- 5 files changed, 13 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index f099af0b41af..235e2b06ac05 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -460,7 +460,7 @@ static int device_add_attrs(struct bus_type *bus, struct device *dev) if (!bus->dev_attrs) return 0; - for (i = 0; attr_name(bus->dev_attrs[i]); i++) { + for (i = 0; bus->dev_attrs[i].attr.name; i++) { error = device_create_file(dev, &bus->dev_attrs[i]); if (error) { while (--i >= 0) @@ -476,7 +476,7 @@ static void device_remove_attrs(struct bus_type *bus, struct device *dev) int i; if (bus->dev_attrs) { - for (i = 0; attr_name(bus->dev_attrs[i]); i++) + for (i = 0; bus->dev_attrs[i].attr.name; i++) device_remove_file(dev, &bus->dev_attrs[i]); } } @@ -596,7 +596,7 @@ static int driver_add_attrs(struct bus_type *bus, struct device_driver *drv) int i; if (bus->drv_attrs) { - for (i = 0; attr_name(bus->drv_attrs[i]); i++) { + for (i = 0; bus->drv_attrs[i].attr.name; i++) { error = driver_create_file(drv, &bus->drv_attrs[i]); if (error) goto err; @@ -616,7 +616,7 @@ static void driver_remove_attrs(struct bus_type *bus, int i; if (bus->drv_attrs) { - for (i = 0; attr_name(bus->drv_attrs[i]); i++) + for (i = 0; bus->drv_attrs[i].attr.name; i++) driver_remove_file(drv, &bus->drv_attrs[i]); } } @@ -857,7 +857,7 @@ static int bus_add_attrs(struct bus_type *bus) int i; if (bus->bus_attrs) { - for (i = 0; attr_name(bus->bus_attrs[i]); i++) { + for (i = 0; bus->bus_attrs[i].attr.name; i++) { error = bus_create_file(bus, &bus->bus_attrs[i]); if (error) goto err; @@ -876,7 +876,7 @@ static void bus_remove_attrs(struct bus_type *bus) int i; if (bus->bus_attrs) { - for (i = 0; attr_name(bus->bus_attrs[i]); i++) + for (i = 0; bus->bus_attrs[i].attr.name; i++) bus_remove_file(bus, &bus->bus_attrs[i]); } } diff --git a/drivers/base/class.c b/drivers/base/class.c index 3ce845471327..8b7818b80056 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -135,7 +135,7 @@ static int add_class_attrs(struct class *cls) int error = 0; if (cls->class_attrs) { - for (i = 0; attr_name(cls->class_attrs[i]); i++) { + for (i = 0; cls->class_attrs[i].attr.name; i++) { error = class_create_file(cls, &cls->class_attrs[i]); if (error) goto error; @@ -154,7 +154,7 @@ static void remove_class_attrs(struct class *cls) int i; if (cls->class_attrs) { - for (i = 0; attr_name(cls->class_attrs[i]); i++) + for (i = 0; cls->class_attrs[i].attr.name; i++) class_remove_file(cls, &cls->class_attrs[i]); } } diff --git a/drivers/base/core.c b/drivers/base/core.c index d743dcee1e77..ae1acdf21bd3 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -440,7 +440,7 @@ static int device_add_attributes(struct device *dev, int i; if (attrs) { - for (i = 0; attr_name(attrs[i]); i++) { + for (i = 0; attrs[i].attr.name; i++) { error = device_create_file(dev, &attrs[i]); if (error) break; @@ -458,7 +458,7 @@ static void device_remove_attributes(struct device *dev, int i; if (attrs) - for (i = 0; attr_name(attrs[i]); i++) + for (i = 0; attrs[i].attr.name; i++) device_remove_file(dev, &attrs[i]); } @@ -469,7 +469,7 @@ static int device_add_bin_attributes(struct device *dev, int i; if (attrs) { - for (i = 0; attr_name(attrs[i]); i++) { + for (i = 0; attrs[i].attr.name; i++) { error = device_create_bin_file(dev, &attrs[i]); if (error) break; @@ -487,7 +487,7 @@ static void device_remove_bin_attributes(struct device *dev, int i; if (attrs) - for (i = 0; attr_name(attrs[i]); i++) + for (i = 0; attrs[i].attr.name; i++) device_remove_bin_file(dev, &attrs[i]); } diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index fa322409bff3..52b2adf63cbf 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -9478,7 +9478,7 @@ static struct niu_parent *niu_new_parent(struct niu *np, if (IS_ERR(plat_dev)) return NULL; - for (i = 0; attr_name(niu_parent_attributes[i]); i++) { + for (i = 0; niu_parent_attributes[i].attr.name; i++) { int err = device_create_file(&plat_dev->dev, &niu_parent_attributes[i]); if (err) diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index a864b8d918ee..b5a9d9b26bd4 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -108,8 +108,6 @@ static const struct attribute_group _name##_group = { \ }; \ __ATTRIBUTE_GROUPS(_name) -#define attr_name(_attr) (_attr).attr.name - struct file; struct vm_area_struct; -- cgit v1.2.3-71-gd317 From 699c1985587aad3432c5ae19801efb4186db8b7a Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Tue, 20 Aug 2013 16:41:02 +0200 Subject: PCI: Add pcibios_pm_ops for optional arch-specific hibernate functionality Platforms may want to provide architecture-specific functionality when a PCI device is doing a hibernate transition. Add a weak symbol pcibios_pm_ops that architectures can override to do so. [bhelgaas: fold in return value checks from v2 patch] Signed-off-by: Sebastian Ott Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 4 ++++ 2 files changed, 47 insertions(+) (limited to 'include') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index e6515e21afa3..98f7b9b89507 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -763,6 +763,13 @@ static int pci_pm_resume(struct device *dev) #ifdef CONFIG_HIBERNATE_CALLBACKS + +/* + * pcibios_pm_ops - provide arch-specific hooks when a PCI device is doing + * a hibernate transition + */ +struct dev_pm_ops __weak pcibios_pm_ops; + static int pci_pm_freeze(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -786,6 +793,9 @@ static int pci_pm_freeze(struct device *dev) return error; } + if (pcibios_pm_ops.freeze) + return pcibios_pm_ops.freeze(dev); + return 0; } @@ -811,6 +821,9 @@ static int pci_pm_freeze_noirq(struct device *dev) pci_pm_set_unknown_state(pci_dev); + if (pcibios_pm_ops.freeze_noirq) + return pcibios_pm_ops.freeze_noirq(dev); + return 0; } @@ -820,6 +833,12 @@ static int pci_pm_thaw_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pcibios_pm_ops.thaw_noirq) { + error = pcibios_pm_ops.thaw_noirq(dev); + if (error) + return error; + } + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); @@ -837,6 +856,12 @@ static int pci_pm_thaw(struct device *dev) const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; + if (pcibios_pm_ops.thaw) { + error = pcibios_pm_ops.thaw(dev); + if (error) + return error; + } + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev); @@ -878,6 +903,9 @@ static int pci_pm_poweroff(struct device *dev) Fixup: pci_fixup_device(pci_fixup_suspend, pci_dev); + if (pcibios_pm_ops.poweroff) + return pcibios_pm_ops.poweroff(dev); + return 0; } @@ -911,6 +939,9 @@ static int pci_pm_poweroff_noirq(struct device *dev) if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI) pci_write_config_word(pci_dev, PCI_COMMAND, 0); + if (pcibios_pm_ops.poweroff_noirq) + return pcibios_pm_ops.poweroff_noirq(dev); + return 0; } @@ -920,6 +951,12 @@ static int pci_pm_restore_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pcibios_pm_ops.restore_noirq) { + error = pcibios_pm_ops.restore_noirq(dev); + if (error) + return error; + } + pci_pm_default_resume_early(pci_dev); if (pci_has_legacy_pm_support(pci_dev)) @@ -937,6 +974,12 @@ static int pci_pm_restore(struct device *dev) const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; + if (pcibios_pm_ops.restore) { + error = pcibios_pm_ops.restore(dev); + if (error) + return error; + } + /* * This is necessary for the hibernation error path in which restore is * called without restoring the standard config registers of the device. diff --git a/include/linux/pci.h b/include/linux/pci.h index 0fd1f1582fa1..89ed12379f2f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1648,6 +1648,10 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, int pcibios_add_device(struct pci_dev *dev); void pcibios_release_device(struct pci_dev *dev); +#ifdef CONFIG_HIBERNATE_CALLBACKS +extern struct dev_pm_ops pcibios_pm_ops; +#endif + #ifdef CONFIG_PCI_MMCONFIG void __init pci_mmcfg_early_init(void); void __init pci_mmcfg_late_init(void); -- cgit v1.2.3-71-gd317 From 2771399ac9986c75437a83b1c723493cfcdfa439 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Thu, 22 Aug 2013 21:55:13 +0200 Subject: fs_enet: cleanup clock API use make the Freescale ethernet driver get, prepare and enable the FEC clock during probe(); disable and unprepare the clock upon remove(), put is done by the devm approach; hold a reference to the clock over the period of use. clock lookup is non-fatal as not all platforms provide clock specs in their device tree; failure to enable specified clocks is fatal. Signed-off-by: Gerhard Sittig Signed-off-by: Anatolij Gustschin Signed-off-by: David S. Miller --- .../net/ethernet/freescale/fs_enet/fs_enet-main.c | 20 ++++++++++++++++++++ include/linux/fs_enet_pd.h | 3 +++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index c04eb3aeb834..6b60582ce8cf 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -999,6 +999,8 @@ static int fs_enet_probe(struct platform_device *ofdev) struct fs_enet_private *fep; struct fs_platform_info *fpi; const u32 *data; + struct clk *clk; + int err; const u8 *mac_addr; const char *phy_connection_type; int privsize, len, ret = -ENODEV; @@ -1036,6 +1038,20 @@ static int fs_enet_probe(struct platform_device *ofdev) fpi->use_rmii = 1; } + /* make clock lookup non-fatal (the driver is shared among platforms), + * but require enable to succeed when a clock was specified/found, + * keep a reference to the clock upon successful acquisition + */ + clk = devm_clk_get(&ofdev->dev, "per"); + if (!IS_ERR(clk)) { + err = clk_prepare_enable(clk); + if (err) { + ret = err; + goto out_free_fpi; + } + fpi->clk_per = clk; + } + privsize = sizeof(*fep) + sizeof(struct sk_buff **) * (fpi->rx_ring + fpi->tx_ring); @@ -1107,6 +1123,8 @@ out_free_dev: free_netdev(ndev); out_put: of_node_put(fpi->phy_node); + if (fpi->clk_per) + clk_disable_unprepare(fpi->clk_per); out_free_fpi: kfree(fpi); return ret; @@ -1123,6 +1141,8 @@ static int fs_enet_remove(struct platform_device *ofdev) fep->ops->cleanup_data(ndev); dev_set_drvdata(fep->dev, NULL); of_node_put(fep->fpi->phy_node); + if (fep->fpi->clk_per) + clk_disable_unprepare(fep->fpi->clk_per); free_netdev(ndev); return 0; } diff --git a/include/linux/fs_enet_pd.h b/include/linux/fs_enet_pd.h index 343d82a54468..efb05961bdd8 100644 --- a/include/linux/fs_enet_pd.h +++ b/include/linux/fs_enet_pd.h @@ -16,6 +16,7 @@ #ifndef FS_ENET_PD_H #define FS_ENET_PD_H +#include #include #include #include @@ -143,6 +144,8 @@ struct fs_platform_info { int use_rmii; /* use RMII mode */ int has_phy; /* if the network is phy container as well...*/ + + struct clk *clk_per; /* 'per' clock for register access */ }; struct fs_mii_fec_platform_info { u32 irq[32]; -- cgit v1.2.3-71-gd317 From 71ea148370f8b6c745a8a42f6fd983cf5ebade18 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 10 Aug 2013 10:46:50 +0300 Subject: dmaengine: make dma_submit_error() return an error code The problem here is that the dma_xfer() functions in drivers/ata/pata_arasan_cf.c and drivers/mtd/nand/fsmc_nand.c expect dma_submit_error() to return an error code so they return 1 when they intended to return a negative. So far as I can tell, none of the ->tx_submit() functions ever do return error codes so this patch should have no effect in the current code. I also changed it from a define to an inline. Signed-off-by: Dan Carpenter Signed-off-by: Dan Williams --- include/linux/dmaengine.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index cb286b1acdb6..b3ba7e410943 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -38,7 +38,10 @@ typedef s32 dma_cookie_t; #define DMA_MIN_COOKIE 1 #define DMA_MAX_COOKIE INT_MAX -#define dma_submit_error(cookie) ((cookie) < 0 ? 1 : 0) +static inline int dma_submit_error(dma_cookie_t cookie) +{ + return cookie < 0 ? cookie : 0; +} /** * enum dma_status - DMA transaction status -- cgit v1.2.3-71-gd317 From 3287c24088abded9f111ca797fdd36f86912d199 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 21 Aug 2013 16:53:37 +0530 Subject: pinctrl: utils : add support to pass config type in generic util APIs Add support to pass the config type like GROUP or PIN when using the utils or generic pin configuration APIs. This will make the APIs more generic. Added additional inline APIs such that it can be use directly as callback for the pinctrl_ops. Changes from V1: - Remove separate implementation for pins and group for pinctrl_utils_dt_free_map and improve this function to support both i.e. PINS and GROUPs. Signed-off-by: Laxman Dewangan Reviewed-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Linus Walleij --- drivers/pinctrl/pinconf-generic.c | 9 +++++---- drivers/pinctrl/pinctrl-palmas.c | 2 +- drivers/pinctrl/pinctrl-utils.c | 12 +++++++++--- include/linux/pinctrl/pinconf-generic.h | 22 ++++++++++++++++++++-- 4 files changed, 35 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index d9536caa9c41..2c62225bafab 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -240,7 +240,8 @@ out: int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **map, - unsigned *reserved_maps, unsigned *num_maps) + unsigned *reserved_maps, unsigned *num_maps, + enum pinctrl_map_type type) { int ret; const char *function; @@ -295,7 +296,7 @@ int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, if (num_configs) { ret = pinctrl_utils_add_map_configs(pctldev, map, reserved_maps, num_maps, group, configs, - num_configs, PIN_MAP_TYPE_CONFIGS_PIN); + num_configs, type); if (ret < 0) goto exit; } @@ -310,7 +311,7 @@ EXPORT_SYMBOL_GPL(pinconf_generic_dt_subnode_to_map); int pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, - unsigned *num_maps) + unsigned *num_maps, enum pinctrl_map_type type) { unsigned reserved_maps; struct device_node *np; @@ -322,7 +323,7 @@ int pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev, for_each_child_of_node(np_config, np) { ret = pinconf_generic_dt_subnode_to_map(pctldev, np, map, - &reserved_maps, num_maps); + &reserved_maps, num_maps, type); if (ret < 0) { pinctrl_utils_dt_free_map(pctldev, *map, *num_maps); return ret; diff --git a/drivers/pinctrl/pinctrl-palmas.c b/drivers/pinctrl/pinctrl-palmas.c index 2697c2efd74b..9550c33b2544 100644 --- a/drivers/pinctrl/pinctrl-palmas.c +++ b/drivers/pinctrl/pinctrl-palmas.c @@ -655,7 +655,7 @@ static const struct pinctrl_ops palmas_pinctrl_ops = { .get_groups_count = palmas_pinctrl_get_groups_count, .get_group_name = palmas_pinctrl_get_group_name, .get_group_pins = palmas_pinctrl_get_group_pins, - .dt_node_to_map = pinconf_generic_dt_node_to_map, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, .dt_free_map = pinctrl_utils_dt_free_map, }; diff --git a/drivers/pinctrl/pinctrl-utils.c b/drivers/pinctrl/pinctrl-utils.c index b7ac646c43ba..48277e025f84 100644 --- a/drivers/pinctrl/pinctrl-utils.c +++ b/drivers/pinctrl/pinctrl-utils.c @@ -126,10 +126,16 @@ void pinctrl_utils_dt_free_map(struct pinctrl_dev *pctldev, { int i; - for (i = 0; i < num_maps; i++) - if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP) + for (i = 0; i < num_maps; i++) { + switch (map[i].type) { + case PIN_MAP_TYPE_CONFIGS_GROUP: + case PIN_MAP_TYPE_CONFIGS_PIN: kfree(map[i].data.configs.configs); - + break; + default: + break; + } + } kfree(map); } EXPORT_SYMBOL_GPL(pinctrl_utils_dt_free_map); diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h index 83f5179ea0c5..fb90ef5eb038 100644 --- a/include/linux/pinctrl/pinconf-generic.h +++ b/include/linux/pinctrl/pinconf-generic.h @@ -140,15 +140,33 @@ static inline unsigned long pinconf_to_config_packed(enum pin_config_param param #ifdef CONFIG_OF #include +#include struct pinctrl_dev; struct pinctrl_map; int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **map, - unsigned *reserved_maps, unsigned *num_maps); + unsigned *reserved_maps, unsigned *num_maps, + enum pinctrl_map_type type); int pinconf_generic_dt_node_to_map(struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, - unsigned *num_maps); + unsigned *num_maps, enum pinctrl_map_type type); + +static inline int pinconf_generic_dt_node_to_map_group( + struct pinctrl_dev *pctldev, struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps) +{ + return pinconf_generic_dt_node_to_map(pctldev, np_config, map, num_maps, + PIN_MAP_TYPE_CONFIGS_GROUP); +} + +static inline int pinconf_generic_dt_node_to_map_pin( + struct pinctrl_dev *pctldev, struct device_node *np_config, + struct pinctrl_map **map, unsigned *num_maps) +{ + return pinconf_generic_dt_node_to_map(pctldev, np_config, map, num_maps, + PIN_MAP_TYPE_CONFIGS_PIN); +} #endif -- cgit v1.2.3-71-gd317 From 687b81d083c082bc1e853032e3a2a54f8c251d27 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 11 Jul 2013 12:56:15 +0100 Subject: i2c: move OF helpers into the core I2C of helpers used to live in of_i2c.c but experience (from SPI) shows that it is much cleaner to have this in the core. This also removes a circular dependency between the helpers and the core, and so we can finally register child nodes in the core instead of doing this manually in each driver. So, fix the drivers and documentation, too. Signed-off-by: Wolfram Sang --- Documentation/acpi/enumeration.txt | 1 - arch/powerpc/platforms/44x/warp.c | 1 - drivers/gpu/drm/tilcdc/tilcdc_slave.c | 1 - drivers/gpu/drm/tilcdc/tilcdc_tfp410.c | 1 - drivers/gpu/host1x/drm/output.c | 2 +- drivers/i2c/busses/i2c-at91.c | 3 - drivers/i2c/busses/i2c-cpm.c | 6 -- drivers/i2c/busses/i2c-davinci.c | 2 - drivers/i2c/busses/i2c-designware-platdrv.c | 2 - drivers/i2c/busses/i2c-gpio.c | 3 - drivers/i2c/busses/i2c-i801.c | 2 - drivers/i2c/busses/i2c-ibm_iic.c | 4 - drivers/i2c/busses/i2c-imx.c | 3 - drivers/i2c/busses/i2c-mpc.c | 2 - drivers/i2c/busses/i2c-mv64xxx.c | 3 - drivers/i2c/busses/i2c-mxs.c | 3 - drivers/i2c/busses/i2c-nomadik.c | 3 - drivers/i2c/busses/i2c-ocores.c | 3 - drivers/i2c/busses/i2c-octeon.c | 3 - drivers/i2c/busses/i2c-omap.c | 3 - drivers/i2c/busses/i2c-pnx.c | 3 - drivers/i2c/busses/i2c-powermac.c | 9 +- drivers/i2c/busses/i2c-pxa.c | 2 - drivers/i2c/busses/i2c-s3c2410.c | 2 - drivers/i2c/busses/i2c-sh_mobile.c | 2 - drivers/i2c/busses/i2c-sirf.c | 3 - drivers/i2c/busses/i2c-stu300.c | 2 - drivers/i2c/busses/i2c-tegra.c | 3 - drivers/i2c/busses/i2c-versatile.c | 2 - drivers/i2c/busses/i2c-wmt.c | 3 - drivers/i2c/busses/i2c-xiic.c | 3 - drivers/i2c/i2c-core.c | 109 +++++++++++++++++++++- drivers/i2c/i2c-mux.c | 3 - drivers/i2c/muxes/i2c-arb-gpio-challenge.c | 1 - drivers/i2c/muxes/i2c-mux-gpio.c | 1 - drivers/i2c/muxes/i2c-mux-pinctrl.c | 1 - drivers/media/platform/exynos4-is/fimc-is-i2c.c | 4 +- drivers/media/platform/exynos4-is/fimc-is.c | 2 +- drivers/media/platform/exynos4-is/media-dev.c | 1 - drivers/of/Kconfig | 6 -- drivers/of/Makefile | 1 - drivers/of/of_i2c.c | 114 ------------------------ drivers/staging/imx-drm/imx-tve.c | 2 +- include/linux/i2c.h | 20 +++++ include/linux/of_i2c.h | 46 ---------- sound/soc/fsl/imx-sgtl5000.c | 2 +- sound/soc/fsl/imx-wm8962.c | 2 +- 47 files changed, 138 insertions(+), 262 deletions(-) delete mode 100644 drivers/of/of_i2c.c delete mode 100644 include/linux/of_i2c.h (limited to 'include') diff --git a/Documentation/acpi/enumeration.txt b/Documentation/acpi/enumeration.txt index d9be7a97dff3..958266efcc20 100644 --- a/Documentation/acpi/enumeration.txt +++ b/Documentation/acpi/enumeration.txt @@ -238,7 +238,6 @@ An I2C bus (controller) driver does: if (ret) /* handle error */ - of_i2c_register_devices(adapter); /* Enumerate the slave devices behind this bus via ACPI */ acpi_i2c_register_devices(adapter); diff --git a/arch/powerpc/platforms/44x/warp.c b/arch/powerpc/platforms/44x/warp.c index 4cfa49901c02..534574a97ec9 100644 --- a/arch/powerpc/platforms/44x/warp.c +++ b/arch/powerpc/platforms/44x/warp.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c index dfffaf014022..a19f657dfa59 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave.c @@ -16,7 +16,6 @@ */ #include -#include #include #include #include diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c index 925c7cddeff9..c38b56b268ac 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c @@ -16,7 +16,6 @@ */ #include -#include #include #include #include diff --git a/drivers/gpu/host1x/drm/output.c b/drivers/gpu/host1x/drm/output.c index 8140fc6c34d8..137ae81ab80e 100644 --- a/drivers/gpu/host1x/drm/output.c +++ b/drivers/gpu/host1x/drm/output.c @@ -9,7 +9,7 @@ #include #include -#include +#include #include "drm.h" diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 6bb839b688be..fd059308affa 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -775,8 +774,6 @@ static int at91_twi_probe(struct platform_device *pdev) return rc; } - of_i2c_register_devices(&dev->adapter); - dev_info(dev->dev, "AT91 i2c bus driver.\n"); return 0; } diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index 2e1f7eb55bf4..b2b8aa9adc0e 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include @@ -681,11 +680,6 @@ static int cpm_i2c_probe(struct platform_device *ofdev) dev_dbg(&ofdev->dev, "hw routines for %s registered.\n", cpm->adap.name); - /* - * register OF I2C devices - */ - of_i2c_register_devices(&cpm->adap); - return 0; out_shut: cpm_i2c_shutdown(cpm); diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index cf90bfff9676..57473415be10 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include @@ -726,7 +725,6 @@ static int davinci_i2c_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failure adding adapter\n"); goto err_unuse_clocks; } - of_i2c_register_devices(adap); return 0; diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 64ffb908641c..ded77c3bd59c 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -172,7 +171,6 @@ static int dw_i2c_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failure adding adapter\n"); return r; } - of_i2c_register_devices(adap); acpi_i2c_register_devices(adap); pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 8cdb4f743e19..bfa02c6c2dda 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -16,7 +16,6 @@ #include #include #include -#include struct i2c_gpio_private_data { struct i2c_adapter adap; @@ -224,8 +223,6 @@ static int i2c_gpio_probe(struct platform_device *pdev) if (ret) goto err_add_bus; - of_i2c_register_devices(adap); - platform_set_drvdata(pdev, priv); dev_info(&pdev->dev, "using pins %u (SDA) and %u (SCL%s)\n", diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 4ebceed6bc66..4296d1721272 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -87,7 +87,6 @@ #include #include #include -#include #if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \ defined CONFIG_DMI @@ -1230,7 +1229,6 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) goto exit_free_irq; } - of_i2c_register_devices(&priv->adapter); i801_probe_optional_slaves(priv); /* We ignore errors - multiplexing is optional */ i801_add_mux(priv); diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index 973f51688276..ff3caa0c28cd 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -42,7 +42,6 @@ #include #include #include -#include #include "i2c-ibm_iic.h" @@ -759,9 +758,6 @@ static int iic_probe(struct platform_device *ofdev) dev_info(&ofdev->dev, "using %s mode\n", dev->fast_mode ? "fast (400 kHz)" : "standard (100 kHz)"); - /* Now register all the child nodes */ - of_i2c_register_devices(adap); - return 0; error_cleanup: diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index a231d2fd91ce..ccf46656bdad 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -52,7 +52,6 @@ #include #include #include -#include #include /** Defines ******************************************************************** @@ -682,8 +681,6 @@ static int __init i2c_imx_probe(struct platform_device *pdev) return ret; } - of_i2c_register_devices(&i2c_imx->adapter); - /* Set up platform driver data */ platform_set_drvdata(pdev, i2c_imx); clk_disable_unprepare(i2c_imx->clk); diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index ffac2145b94a..f4060939e959 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -694,7 +693,6 @@ static int fsl_i2c_probe(struct platform_device *op) dev_err(i2c->dev, "failed to add adapter\n"); goto fail_add; } - of_i2c_register_devices(&i2c->adap); return result; diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index bc60f9ac7c04..7f3a47443494 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -871,8 +870,6 @@ mv64xxx_i2c_probe(struct platform_device *pd) goto exit_free_irq; } - of_i2c_register_devices(&drv_data->adapter); - return 0; exit_free_irq: diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 46cda0b02fd1..f4a01675fa71 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -752,8 +751,6 @@ static int mxs_i2c_probe(struct platform_device *pdev) return err; } - of_i2c_register_devices(adap); - return 0; } diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 1909e8073541..8bf9ac01301a 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #define DRIVER_NAME "nmk-i2c" @@ -1045,8 +1044,6 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id) goto err_add_adap; } - of_i2c_register_devices(adap); - pm_runtime_put(&adev->dev); return 0; diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index e14182cd87ff..c61f37a10a07 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -24,7 +24,6 @@ #include #include #include -#include #include struct ocores_i2c { @@ -432,8 +431,6 @@ static int ocores_i2c_probe(struct platform_device *pdev) if (pdata) { for (i = 0; i < pdata->num_devices; i++) i2c_new_device(&i2c->adap, pdata->devices + i); - } else { - of_i2c_register_devices(&i2c->adap); } return 0; diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 956fe320f313..b929ba271b47 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -599,8 +598,6 @@ static int octeon_i2c_probe(struct platform_device *pdev) } dev_info(i2c->dev, "version %s\n", DRV_VERSION); - of_i2c_register_devices(&i2c->adap); - return 0; out: diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 9ccb7b9cb6fc..6d8308d5dc4e 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -1231,8 +1230,6 @@ omap_i2c_probe(struct platform_device *pdev) dev_info(dev->dev, "bus %d rev%d.%d at %d kHz\n", adap->nr, major, minor, dev->speed); - of_i2c_register_devices(adap); - pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index 1713b3ee61f5..d09577a05d00 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -23,7 +23,6 @@ #include #include #include -#include #define I2C_PNX_TIMEOUT_DEFAULT 10 /* msec */ #define I2C_PNX_SPEED_KHZ_DEFAULT 100 @@ -741,8 +740,6 @@ static int i2c_pnx_probe(struct platform_device *pdev) goto out_irq; } - of_i2c_register_devices(&alg_data->adapter); - dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n", alg_data->adapter.name, res->start, alg_data->irq); diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 06858d576552..37e8cfad625b 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c @@ -440,7 +440,9 @@ static int i2c_powermac_probe(struct platform_device *dev) adapter->algo = &i2c_powermac_algorithm; i2c_set_adapdata(adapter, bus); adapter->dev.parent = &dev->dev; - adapter->dev.of_node = dev->dev.of_node; + + /* Clear of_node to skip automatic registration of i2c child nodes */ + adapter->dev.of_node = NULL; rc = i2c_add_adapter(adapter); if (rc) { printk(KERN_ERR "i2c-powermac: Adapter %s registration " @@ -451,9 +453,8 @@ static int i2c_powermac_probe(struct platform_device *dev) printk(KERN_INFO "PowerMac i2c bus %s registered\n", adapter->name); - /* Cannot use of_i2c_register_devices() due to Apple device-tree - * funkyness - */ + /* Use custom child registration due to Apple device-tree funkyness */ + adapter->dev.of_node = dev->dev.of_node; i2c_powermac_register_devices(adapter, bus); return 0; diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 3dbc1acdc28a..bbe6dfbc5c05 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -1247,7 +1246,6 @@ static int i2c_pxa_probe(struct platform_device *dev) printk(KERN_INFO "I2C: Failed to add bus\n"); goto eadapt; } - of_i2c_register_devices(&i2c->adap); platform_set_drvdata(dev, i2c); diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 0a077b1ef94f..3535f3c0f7b4 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include @@ -1154,7 +1153,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) return ret; } - of_i2c_register_devices(&i2c->adap); platform_set_drvdata(pdev, i2c); pm_runtime_enable(&pdev->dev); diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 4e86a3190d46..55110ddbed1f 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -758,7 +757,6 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) "I2C adapter %d with bus speed %lu Hz (L/H=%x/%x)\n", adap->nr, pd->bus_speed, pd->iccl, pd->icch); - of_i2c_register_devices(adap); return 0; err_all: diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c index a63c7d506836..0ff22e29e7df 100644 --- a/drivers/i2c/busses/i2c-sirf.c +++ b/drivers/i2c/busses/i2c-sirf.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -366,8 +365,6 @@ static int i2c_sirfsoc_probe(struct platform_device *pdev) clk_disable(clk); - of_i2c_register_devices(adap); - dev_info(&pdev->dev, " I2C adapter ready to operate\n"); return 0; diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c index 19a40afaf172..f8f6f2e552db 100644 --- a/drivers/i2c/busses/i2c-stu300.c +++ b/drivers/i2c/busses/i2c-stu300.c @@ -17,7 +17,6 @@ #include #include #include -#include /* the name of this kernel module */ #define NAME "stu300" @@ -933,7 +932,6 @@ stu300_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); dev_info(&pdev->dev, "ST DDC I2C @ %p, irq %d\n", dev->virtbase, dev->irq); - of_i2c_register_devices(adap); return 0; } diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 9aa1b60f7fdd..c457cb447c66 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -802,8 +801,6 @@ static int tegra_i2c_probe(struct platform_device *pdev) return ret; } - of_i2c_register_devices(&i2c_dev->adapter); - return 0; } diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c index f3a8790a07e8..6bb3a89a440f 100644 --- a/drivers/i2c/busses/i2c-versatile.c +++ b/drivers/i2c/busses/i2c-versatile.c @@ -16,7 +16,6 @@ #include #include #include -#include #define I2C_CONTROL 0x00 #define I2C_CONTROLS 0x00 @@ -108,7 +107,6 @@ static int i2c_versatile_probe(struct platform_device *dev) ret = i2c_bit_add_numbered_bus(&i2c->adap); if (ret >= 0) { platform_set_drvdata(dev, i2c); - of_i2c_register_devices(&i2c->adap); return 0; } diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c index baaa7d15b73e..c65da3d913a0 100644 --- a/drivers/i2c/busses/i2c-wmt.c +++ b/drivers/i2c/busses/i2c-wmt.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -439,8 +438,6 @@ static int wmt_i2c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, i2c_dev); - of_i2c_register_devices(adap); - return 0; } diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index 433f377b3869..4c8b368d463b 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -40,7 +40,6 @@ #include #include #include -#include #define DRIVER_NAME "xiic-i2c" @@ -752,8 +751,6 @@ static int xiic_i2c_probe(struct platform_device *pdev) i2c_new_device(&i2c->adap, pdata->devices + i); } - of_i2c_register_devices(&i2c->adap); - return 0; add_adapter_failed: diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 3d9ca2d3d77e..e874b052b4f8 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -23,7 +23,11 @@ SMBus 2.0 support by Mark Studebaker and Jean Delvare Mux support by Rodolfo Giometti and - Michael Lawnick */ + Michael Lawnick + OF support is copyright (c) 2008 Jochen Friedrich + (based on a previous patch from Jon Smirl ) and + (c) 2013 Wolfram Sang + */ #include #include @@ -35,7 +39,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -954,6 +960,104 @@ static void i2c_scan_static_board_info(struct i2c_adapter *adapter) up_read(&__i2c_board_lock); } +/* OF support code */ + +#if IS_ENABLED(CONFIG_OF) +static void of_i2c_register_devices(struct i2c_adapter *adap) +{ + void *result; + struct device_node *node; + + /* Only register child devices if the adapter has a node pointer set */ + if (!adap->dev.of_node) + return; + + dev_dbg(&adap->dev, "of_i2c: walking child nodes\n"); + + for_each_available_child_of_node(adap->dev.of_node, node) { + struct i2c_board_info info = {}; + struct dev_archdata dev_ad = {}; + const __be32 *addr; + int len; + + dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name); + + if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) { + dev_err(&adap->dev, "of_i2c: modalias failure on %s\n", + node->full_name); + continue; + } + + addr = of_get_property(node, "reg", &len); + if (!addr || (len < sizeof(int))) { + dev_err(&adap->dev, "of_i2c: invalid reg on %s\n", + node->full_name); + continue; + } + + info.addr = be32_to_cpup(addr); + if (info.addr > (1 << 10) - 1) { + dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n", + info.addr, node->full_name); + continue; + } + + info.irq = irq_of_parse_and_map(node, 0); + info.of_node = of_node_get(node); + info.archdata = &dev_ad; + + if (of_get_property(node, "wakeup-source", NULL)) + info.flags |= I2C_CLIENT_WAKE; + + request_module("%s%s", I2C_MODULE_PREFIX, info.type); + + result = i2c_new_device(adap, &info); + if (result == NULL) { + dev_err(&adap->dev, "of_i2c: Failure registering %s\n", + node->full_name); + of_node_put(node); + irq_dispose_mapping(info.irq); + continue; + } + } +} + +static int of_dev_node_match(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +/* must call put_device() when done with returned i2c_client device */ +struct i2c_client *of_find_i2c_device_by_node(struct device_node *node) +{ + struct device *dev; + + dev = bus_find_device(&i2c_bus_type, NULL, node, + of_dev_node_match); + if (!dev) + return NULL; + + return i2c_verify_client(dev); +} +EXPORT_SYMBOL(of_find_i2c_device_by_node); + +/* must call put_device() when done with returned i2c_adapter device */ +struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node) +{ + struct device *dev; + + dev = bus_find_device(&i2c_bus_type, NULL, node, + of_dev_node_match); + if (!dev) + return NULL; + + return i2c_verify_adapter(dev); +} +EXPORT_SYMBOL(of_find_i2c_adapter_by_node); +#else +static void of_i2c_register_devices(struct i2c_adapter *adap) { } +#endif /* CONFIG_OF */ + static int i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap) { @@ -1058,6 +1162,8 @@ static int i2c_register_adapter(struct i2c_adapter *adap) exit_recovery: /* create pre-declared device nodes */ + of_i2c_register_devices(adap); + if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap); @@ -1282,7 +1388,6 @@ void i2c_del_adapter(struct i2c_adapter *adap) } EXPORT_SYMBOL(i2c_del_adapter); - /* ------------------------------------------------------------------------- */ int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *)) diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 7409ebb33c47..797e3117bef7 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -25,7 +25,6 @@ #include #include #include -#include /* multiplexer per channel data */ struct i2c_mux_priv { @@ -185,8 +184,6 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent, dev_info(&parent->dev, "Added multiplexed i2c bus %d\n", i2c_adapter_id(&priv->adap)); - of_i2c_register_devices(&priv->adap); - return &priv->adap; } EXPORT_SYMBOL_GPL(i2c_add_mux_adapter); diff --git a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c index f7bf24375f81..74b41ae690f3 100644 --- a/drivers/i2c/muxes/i2c-arb-gpio-challenge.c +++ b/drivers/i2c/muxes/i2c-arb-gpio-challenge.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index 6a206e8d58f4..5d4a99ba743e 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -16,7 +16,6 @@ #include #include #include -#include #include struct gpiomux { diff --git a/drivers/i2c/muxes/i2c-mux-pinctrl.c b/drivers/i2c/muxes/i2c-mux-pinctrl.c index 0d082027c29a..69a91732ae65 100644 --- a/drivers/i2c/muxes/i2c-mux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-mux-pinctrl.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/exynos4-is/fimc-is-i2c.c index 617a798d9235..993055605214 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-i2c.c +++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.c @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include #include @@ -67,8 +67,6 @@ static int fimc_is_i2c_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_enable(&i2c_adap->dev); - of_i2c_register_devices(i2c_adap); - return 0; } diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 967f6a939340..2276fdc213c5 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 19f556c5957f..f8c66b434fd6 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 80e5c13b930d..78cc76053328 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -48,12 +48,6 @@ config OF_IRQ def_bool y depends on !SPARC -config OF_I2C - def_tristate I2C - depends on I2C - help - OpenFirmware I2C accessors - config OF_NET depends on NETDEVICES def_bool y diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 1f9c0c492ef9..efd05102c405 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -3,7 +3,6 @@ obj-$(CONFIG_OF_FLATTREE) += fdt.o obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o obj-$(CONFIG_OF_IRQ) += irq.o -obj-$(CONFIG_OF_I2C) += of_i2c.o obj-$(CONFIG_OF_NET) += of_net.o obj-$(CONFIG_OF_SELFTEST) += selftest.o obj-$(CONFIG_OF_MDIO) += of_mdio.o diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c deleted file mode 100644 index b667264222cc..000000000000 --- a/drivers/of/of_i2c.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * OF helpers for the I2C API - * - * Copyright (c) 2008 Jochen Friedrich - * - * Based on a previous patch from Jon Smirl - * - * 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; either version 2 of the License, or - * (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include - -void of_i2c_register_devices(struct i2c_adapter *adap) -{ - void *result; - struct device_node *node; - - /* Only register child devices if the adapter has a node pointer set */ - if (!adap->dev.of_node) - return; - - dev_dbg(&adap->dev, "of_i2c: walking child nodes\n"); - - for_each_available_child_of_node(adap->dev.of_node, node) { - struct i2c_board_info info = {}; - struct dev_archdata dev_ad = {}; - const __be32 *addr; - int len; - - dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name); - - if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) { - dev_err(&adap->dev, "of_i2c: modalias failure on %s\n", - node->full_name); - continue; - } - - addr = of_get_property(node, "reg", &len); - if (!addr || (len < sizeof(int))) { - dev_err(&adap->dev, "of_i2c: invalid reg on %s\n", - node->full_name); - continue; - } - - info.addr = be32_to_cpup(addr); - if (info.addr > (1 << 10) - 1) { - dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n", - info.addr, node->full_name); - continue; - } - - info.irq = irq_of_parse_and_map(node, 0); - info.of_node = of_node_get(node); - info.archdata = &dev_ad; - - if (of_get_property(node, "wakeup-source", NULL)) - info.flags |= I2C_CLIENT_WAKE; - - request_module("%s%s", I2C_MODULE_PREFIX, info.type); - - result = i2c_new_device(adap, &info); - if (result == NULL) { - dev_err(&adap->dev, "of_i2c: Failure registering %s\n", - node->full_name); - of_node_put(node); - irq_dispose_mapping(info.irq); - continue; - } - } -} -EXPORT_SYMBOL(of_i2c_register_devices); - -static int of_dev_node_match(struct device *dev, void *data) -{ - return dev->of_node == data; -} - -/* must call put_device() when done with returned i2c_client device */ -struct i2c_client *of_find_i2c_device_by_node(struct device_node *node) -{ - struct device *dev; - - dev = bus_find_device(&i2c_bus_type, NULL, node, - of_dev_node_match); - if (!dev) - return NULL; - - return i2c_verify_client(dev); -} -EXPORT_SYMBOL(of_find_i2c_device_by_node); - -/* must call put_device() when done with returned i2c_adapter device */ -struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node) -{ - struct device *dev; - - dev = bus_find_device(&i2c_bus_type, NULL, node, - of_dev_node_match); - if (!dev) - return NULL; - - return i2c_verify_adapter(dev); -} -EXPORT_SYMBOL(of_find_i2c_adapter_by_node); - -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/imx-drm/imx-tve.c b/drivers/staging/imx-drm/imx-tve.c index a56797d88edc..2d76fd47c3d7 100644 --- a/drivers/staging/imx-drm/imx-tve.c +++ b/drivers/staging/imx-drm/imx-tve.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/linux/i2c.h b/include/linux/i2c.h index b3c4b8dac1ce..ed53696e31a6 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -544,6 +544,26 @@ static inline int i2c_adapter_id(struct i2c_adapter *adap) #endif /* I2C */ +#if IS_ENABLED(CONFIG_OF) +/* must call put_device() when done with returned i2c_client device */ +extern struct i2c_client *of_find_i2c_device_by_node(struct device_node *node); + +/* must call put_device() when done with returned i2c_adapter device */ +extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node); + +#else + +static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node) +{ + return NULL; +} + +static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node) +{ + return NULL; +} +#endif /* CONFIG_OF */ + #if IS_ENABLED(CONFIG_ACPI_I2C) extern void acpi_i2c_register_devices(struct i2c_adapter *adap); #else diff --git a/include/linux/of_i2c.h b/include/linux/of_i2c.h deleted file mode 100644 index cfb545cd86b5..000000000000 --- a/include/linux/of_i2c.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Generic I2C API implementation for PowerPC. - * - * Copyright (c) 2008 Jochen Friedrich - * - * 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; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef __LINUX_OF_I2C_H -#define __LINUX_OF_I2C_H - -#if defined(CONFIG_OF_I2C) || defined(CONFIG_OF_I2C_MODULE) -#include - -extern void of_i2c_register_devices(struct i2c_adapter *adap); - -/* must call put_device() when done with returned i2c_client device */ -extern struct i2c_client *of_find_i2c_device_by_node(struct device_node *node); - -/* must call put_device() when done with returned i2c_adapter device */ -extern struct i2c_adapter *of_find_i2c_adapter_by_node( - struct device_node *node); - -#else -static inline void of_i2c_register_devices(struct i2c_adapter *adap) -{ - return; -} - -static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node) -{ - return NULL; -} - -/* must call put_device() when done with returned i2c_adapter device */ -static inline struct i2c_adapter *of_find_i2c_adapter_by_node( - struct device_node *node) -{ - return NULL; -} -#endif /* CONFIG_OF_I2C */ - -#endif /* __LINUX_OF_I2C_H */ diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c index 3f726e4f88db..f2fbde9c3d4c 100644 --- a/sound/soc/fsl/imx-sgtl5000.c +++ b/sound/soc/fsl/imx-sgtl5000.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c index 52a36a90f4f4..9fd7a65ae83b 100644 --- a/sound/soc/fsl/imx-wm8962.c +++ b/sound/soc/fsl/imx-wm8962.c @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include #include -- cgit v1.2.3-71-gd317 From 55e71edb81b2b45273e7b284cce13ff24bde846f Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 21 Aug 2013 17:28:23 +0300 Subject: i2c: move ACPI helpers into the core This follows what has already been done for the DeviceTree helpers. Move the ACPI helpers from drivers/acpi/acpi_i2c.c to the I2C core and update documentation accordingly. This also solves a problem reported by Jerry Snitselaar that we can't build the ACPI I2C helpers as a module. Signed-off-by: Mika Westerberg Acked-by: Rafael J. Wysocki Signed-off-by: Wolfram Sang --- Documentation/acpi/enumeration.txt | 15 +--- drivers/acpi/Kconfig | 6 -- drivers/acpi/Makefile | 1 - drivers/acpi/acpi_i2c.c | 103 ---------------------------- drivers/i2c/busses/i2c-designware-platdrv.c | 1 - drivers/i2c/i2c-core.c | 91 ++++++++++++++++++++++++ include/linux/i2c.h | 6 -- 7 files changed, 94 insertions(+), 129 deletions(-) delete mode 100644 drivers/acpi/acpi_i2c.c (limited to 'include') diff --git a/Documentation/acpi/enumeration.txt b/Documentation/acpi/enumeration.txt index 958266efcc20..d977778b5e67 100644 --- a/Documentation/acpi/enumeration.txt +++ b/Documentation/acpi/enumeration.txt @@ -228,18 +228,9 @@ ACPI handle like: I2C serial bus support ~~~~~~~~~~~~~~~~~~~~~~ The slaves behind I2C bus controller only need to add the ACPI IDs like -with the platform and SPI drivers. However the I2C bus controller driver -needs to call acpi_i2c_register_devices() after it has added the adapter. - -An I2C bus (controller) driver does: - - ... - ret = i2c_add_numbered_adapter(adapter); - if (ret) - /* handle error */ - - /* Enumerate the slave devices behind this bus via ACPI */ - acpi_i2c_register_devices(adapter); +with the platform and SPI drivers. The I2C core automatically enumerates +any slave devices behind the controller device once the adapter is +registered. Below is an example of how to add ACPI support to the existing mpu3050 input driver: diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 100bd724f648..4e0162fa5d36 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -180,12 +180,6 @@ config ACPI_DOCK This driver supports ACPI-controlled docking stations and removable drive bays such as the IBM Ultrabay and the Dell Module Bay. -config ACPI_I2C - def_tristate I2C - depends on I2C - help - ACPI I2C enumeration support. - config ACPI_PROCESSOR tristate "Processor" select THERMAL diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 81dbeb83bb45..cdaf68b58b00 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -73,7 +73,6 @@ obj-$(CONFIG_ACPI_HED) += hed.o obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o -obj-$(CONFIG_ACPI_I2C) += acpi_i2c.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o processor_throttling.o diff --git a/drivers/acpi/acpi_i2c.c b/drivers/acpi/acpi_i2c.c deleted file mode 100644 index a82c7626aa9b..000000000000 --- a/drivers/acpi/acpi_i2c.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * ACPI I2C enumeration support - * - * Copyright (C) 2012, Intel Corporation - * Author: Mika Westerberg - * - * 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 -#include -#include -#include -#include - -ACPI_MODULE_NAME("i2c"); - -static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data) -{ - struct i2c_board_info *info = data; - - if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { - struct acpi_resource_i2c_serialbus *sb; - - sb = &ares->data.i2c_serial_bus; - if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) { - info->addr = sb->slave_address; - if (sb->access_mode == ACPI_I2C_10BIT_MODE) - info->flags |= I2C_CLIENT_TEN; - } - } else if (info->irq < 0) { - struct resource r; - - if (acpi_dev_resource_interrupt(ares, 0, &r)) - info->irq = r.start; - } - - /* Tell the ACPI core to skip this resource */ - return 1; -} - -static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, - void *data, void **return_value) -{ - struct i2c_adapter *adapter = data; - struct list_head resource_list; - struct i2c_board_info info; - struct acpi_device *adev; - int ret; - - if (acpi_bus_get_device(handle, &adev)) - return AE_OK; - if (acpi_bus_get_status(adev) || !adev->status.present) - return AE_OK; - - memset(&info, 0, sizeof(info)); - info.acpi_node.handle = handle; - info.irq = -1; - - INIT_LIST_HEAD(&resource_list); - ret = acpi_dev_get_resources(adev, &resource_list, - acpi_i2c_add_resource, &info); - acpi_dev_free_resource_list(&resource_list); - - if (ret < 0 || !info.addr) - return AE_OK; - - strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type)); - if (!i2c_new_device(adapter, &info)) { - dev_err(&adapter->dev, - "failed to add I2C device %s from ACPI\n", - dev_name(&adev->dev)); - } - - return AE_OK; -} - -/** - * acpi_i2c_register_devices - enumerate I2C slave devices behind adapter - * @adapter: pointer to adapter - * - * Enumerate all I2C slave devices behind this adapter by walking the ACPI - * namespace. When a device is found it will be added to the Linux device - * model and bound to the corresponding ACPI handle. - */ -void acpi_i2c_register_devices(struct i2c_adapter *adapter) -{ - acpi_handle handle; - acpi_status status; - - handle = ACPI_HANDLE(adapter->dev.parent); - if (!handle) - return; - - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, - acpi_i2c_add_device, NULL, - adapter, NULL); - if (ACPI_FAILURE(status)) - dev_warn(&adapter->dev, "failed to enumerate I2C slaves\n"); -} -EXPORT_SYMBOL_GPL(acpi_i2c_register_devices); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index ded77c3bd59c..6e99d9f56dee 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -171,7 +171,6 @@ static int dw_i2c_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failure adding adapter\n"); return r; } - acpi_i2c_register_devices(adap); pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); pm_runtime_use_autosuspend(&pdev->dev); diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index e874b052b4f8..29d3f045a2bf 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -1058,6 +1058,96 @@ EXPORT_SYMBOL(of_find_i2c_adapter_by_node); static void of_i2c_register_devices(struct i2c_adapter *adap) { } #endif /* CONFIG_OF */ +/* ACPI support code */ + +#if IS_ENABLED(CONFIG_ACPI) +static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data) +{ + struct i2c_board_info *info = data; + + if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { + struct acpi_resource_i2c_serialbus *sb; + + sb = &ares->data.i2c_serial_bus; + if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) { + info->addr = sb->slave_address; + if (sb->access_mode == ACPI_I2C_10BIT_MODE) + info->flags |= I2C_CLIENT_TEN; + } + } else if (info->irq < 0) { + struct resource r; + + if (acpi_dev_resource_interrupt(ares, 0, &r)) + info->irq = r.start; + } + + /* Tell the ACPI core to skip this resource */ + return 1; +} + +static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, + void *data, void **return_value) +{ + struct i2c_adapter *adapter = data; + struct list_head resource_list; + struct i2c_board_info info; + struct acpi_device *adev; + int ret; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + if (acpi_bus_get_status(adev) || !adev->status.present) + return AE_OK; + + memset(&info, 0, sizeof(info)); + info.acpi_node.handle = handle; + info.irq = -1; + + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, + acpi_i2c_add_resource, &info); + acpi_dev_free_resource_list(&resource_list); + + if (ret < 0 || !info.addr) + return AE_OK; + + strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type)); + if (!i2c_new_device(adapter, &info)) { + dev_err(&adapter->dev, + "failed to add I2C device %s from ACPI\n", + dev_name(&adev->dev)); + } + + return AE_OK; +} + +/** + * acpi_i2c_register_devices - enumerate I2C slave devices behind adapter + * @adap: pointer to adapter + * + * Enumerate all I2C slave devices behind this adapter by walking the ACPI + * namespace. When a device is found it will be added to the Linux device + * model and bound to the corresponding ACPI handle. + */ +static void acpi_i2c_register_devices(struct i2c_adapter *adap) +{ + acpi_handle handle; + acpi_status status; + + handle = ACPI_HANDLE(adap->dev.parent); + if (!handle) + return; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, + acpi_i2c_add_device, NULL, + adap, NULL); + if (ACPI_FAILURE(status)) + dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); +} +#else +static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) {} +#endif /* CONFIG_ACPI */ + static int i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap) { @@ -1163,6 +1253,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) exit_recovery: /* create pre-declared device nodes */ of_i2c_register_devices(adap); + acpi_i2c_register_devices(adap); if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index ed53696e31a6..2ab11dc38077 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -564,10 +564,4 @@ static inline struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node } #endif /* CONFIG_OF */ -#if IS_ENABLED(CONFIG_ACPI_I2C) -extern void acpi_i2c_register_devices(struct i2c_adapter *adap); -#else -static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) {} -#endif - #endif /* _LINUX_I2C_H */ -- cgit v1.2.3-71-gd317 From eb18cba78c2b9250663021e17e1e9cc34630e92a Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Tue, 20 Aug 2013 15:05:17 -0400 Subject: math64: New separate div64_u64_rem helper Commit f792685006274a850e6cc0ea9ade275ccdfc90bc ("math64: New div64_u64_rem helper") implemented div64_u64 in terms of div64_u64_rem. But div64_u64_rem was removed because it slowed down div64_u64 (and there were no other users of div64_u64_rem). Device Mapper's I/O statistics support has a need for div64_u64_rem; reintroduce this helper as a separate method that doesn't slow down div64_u64, especially on 32-bit systems. Signed-off-by: Mike Snitzer Cc: Stanislaw Gruszka Cc: Ingo Molnar Cc: Frederic Weisbecker Cc: Mikulas Patocka Signed-off-by: Alasdair G Kergon --- include/linux/math64.h | 13 +++++++++++++ lib/div64.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) (limited to 'include') diff --git a/include/linux/math64.h b/include/linux/math64.h index 2913b86eb12a..69ed5f5e9f6e 100644 --- a/include/linux/math64.h +++ b/include/linux/math64.h @@ -30,6 +30,15 @@ static inline s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder) return dividend / divisor; } +/** + * div64_u64_rem - unsigned 64bit divide with 64bit divisor and remainder + */ +static inline u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder) +{ + *remainder = dividend % divisor; + return dividend / divisor; +} + /** * div64_u64 - unsigned 64bit divide with 64bit divisor */ @@ -63,6 +72,10 @@ static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) extern s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder); #endif +#ifndef div64_u64_rem +extern u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder); +#endif + #ifndef div64_u64 extern u64 div64_u64(u64 dividend, u64 divisor); #endif diff --git a/lib/div64.c b/lib/div64.c index a163b6caef73..4382ad77777e 100644 --- a/lib/div64.c +++ b/lib/div64.c @@ -78,6 +78,46 @@ s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder) EXPORT_SYMBOL(div_s64_rem); #endif +/** + * div64_u64_rem - unsigned 64bit divide with 64bit divisor and remainder + * @dividend: 64bit dividend + * @divisor: 64bit divisor + * @remainder: 64bit remainder + * + * This implementation is a comparable to algorithm used by div64_u64. + * But this operation, which includes math for calculating the remainder, + * is kept distinct to avoid slowing down the div64_u64 operation on 32bit + * systems. + */ +#ifndef div64_u64_rem +u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder) +{ + u32 high = divisor >> 32; + u64 quot; + + if (high == 0) { + u32 rem32; + quot = div_u64_rem(dividend, divisor, &rem32); + *remainder = rem32; + } else { + int n = 1 + fls(high); + quot = div_u64(dividend >> n, divisor >> n); + + if (quot != 0) + quot--; + + *remainder = dividend - quot * divisor; + if (*remainder >= divisor) { + quot++; + *remainder -= divisor; + } + } + + return quot; +} +EXPORT_SYMBOL(div64_u64_rem); +#endif + /** * div64_u64 - unsigned 64bit divide with 64bit divisor * @dividend: 64bit dividend -- cgit v1.2.3-71-gd317 From 19504cf5f35fbe85db811fce9f4392a0cbdada2f Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Thu, 15 Aug 2013 14:51:28 +0300 Subject: cfg80211: add flags to cfg80211_rx_mgmt() Add flags intended to report various auxiliary information and introduce the NL80211_RXMGMT_FLAG_ANSWERED flag to report that the frame was already answered by the device. Signed-off-by: Vladimir Kondratiev [REPLIED->ANSWERED, reword commit message] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/wmi.c | 7 +++---- drivers/net/wireless/ath/wil6210/wmi.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 4 ++-- drivers/net/wireless/mwifiex/util.c | 4 ++-- include/net/cfg80211.h | 3 ++- include/uapi/linux/nl80211.h | 16 ++++++++++++++++ net/mac80211/rx.c | 3 +-- net/wireless/mlme.c | 4 ++-- net/wireless/nl80211.c | 6 ++++-- net/wireless/nl80211.h | 2 +- 10 files changed, 34 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 87aefb4c4c23..546d5da0b894 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -568,8 +568,8 @@ static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len, dlen, freq, vif->probe_req_report); if (vif->probe_req_report || vif->nw_type == AP_NETWORK) - cfg80211_rx_mgmt(&vif->wdev, freq, 0, - ev->data, dlen, GFP_ATOMIC); + cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0, + GFP_ATOMIC); return 0; } @@ -608,8 +608,7 @@ static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len, return -EINVAL; } ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq); - cfg80211_rx_mgmt(&vif->wdev, freq, 0, - ev->data, dlen, GFP_ATOMIC); + cfg80211_rx_mgmt(&vif->wdev, freq, 0, ev->data, dlen, 0, GFP_ATOMIC); return 0; } diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index dc8059ad4bab..21c791ee8178 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -339,7 +339,7 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) } } else { cfg80211_rx_mgmt(wil->wdev, freq, signal, - (void *)rx_mgmt_frame, d_len, GFP_KERNEL); + (void *)rx_mgmt_frame, d_len, 0, GFP_KERNEL); } } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 79555f006d53..d7a974532909 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -1430,7 +1430,7 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, IEEE80211_BAND_5GHZ); wdev = &ifp->vif->wdev; - cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, + cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0, GFP_ATOMIC); kfree(mgmt_frame); @@ -1895,7 +1895,7 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ); - cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, + cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0, GFP_ATOMIC); brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n", diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index e57ac0dd3ab5..5d9e150f4111 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -171,8 +171,8 @@ mwifiex_process_mgmt_packet(struct mwifiex_private *priv, rx_pd->rx_pkt_length = cpu_to_le16(pkt_len); cfg80211_rx_mgmt(priv->wdev, priv->roc_cfg.chan.center_freq, - CAL_RSSI(rx_pd->snr, rx_pd->nf), - skb->data, pkt_len, GFP_ATOMIC); + CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len, + 0, GFP_ATOMIC); return 0; } diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9ab7a0690d93..d530c54a3662 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4056,6 +4056,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, * @sig_dbm: signal strength in mBm, or 0 if unknown * @buf: Management frame (header + body) * @len: length of the frame data + * @flags: flags, as defined in enum nl80211_rxmgmt_flags * @gfp: context flags * * This function is called whenever an Action frame is received for a station @@ -4067,7 +4068,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, * driver is responsible for rejecting the frame. */ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_dbm, - const u8 *buf, size_t len, gfp_t gfp); + const u8 *buf, size_t len, u32 flags, gfp_t gfp); /** * cfg80211_mgmt_tx_status - notification of TX status for management frame diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 1f42bc3dcb9c..fde2c021b26d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1493,6 +1493,9 @@ enum nl80211_commands { * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter * field in the probe response (%NL80211_ATTR_PROBE_RESP). * + * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32. + * As specified in the &enum nl80211_rxmgmt_flags. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1801,6 +1804,8 @@ enum nl80211_attrs { NL80211_ATTR_CSA_C_OFF_BEACON, NL80211_ATTR_CSA_C_OFF_PRESP, + NL80211_ATTR_RXMGMT_FLAGS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3901,4 +3906,15 @@ enum nl80211_crit_proto_id { /* maximum duration for critical protocol measures */ #define NL80211_CRIT_PROTO_MAX_DURATION 5000 /* msec */ +/** + * enum nl80211_rxmgmt_flags - flags for received management frame. + * + * Used by cfg80211_rx_mgmt() + * + * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver. + */ +enum nl80211_rxmgmt_flags { + NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index ffad155316a9..07901050812f 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2678,8 +2678,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) sig = status->signal; if (cfg80211_rx_mgmt(&rx->sdata->wdev, status->freq, sig, - rx->skb->data, rx->skb->len, - GFP_ATOMIC)) { + rx->skb->data, rx->skb->len, 0, GFP_ATOMIC)) { if (rx->sta) rx->sta->rx_packets++; dev_kfree_skb(rx->skb); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index bfac5e186f57..8d49c1ce3dea 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -621,7 +621,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, } bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, - const u8 *buf, size_t len, gfp_t gfp) + const u8 *buf, size_t len, u32 flags, gfp_t gfp) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); @@ -664,7 +664,7 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm, /* Indicate the received Action frame to user space */ if (nl80211_send_mgmt(rdev, wdev, reg->nlportid, freq, sig_mbm, - buf, len, gfp)) + buf, len, flags, gfp)) continue; result = true; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 334697de5cc0..a51269d2d488 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -10446,7 +10446,7 @@ EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u32 nlportid, int freq, int sig_dbm, - const u8 *buf, size_t len, gfp_t gfp) + const u8 *buf, size_t len, u32 flags, gfp_t gfp) { struct net_device *netdev = wdev->netdev; struct sk_buff *msg; @@ -10469,7 +10469,9 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) || (sig_dbm && nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) || - nla_put(msg, NL80211_ATTR_FRAME, len, buf)) + nla_put(msg, NL80211_ATTR_FRAME, len, buf) || + (flags && + nla_put_u32(msg, NL80211_ATTR_RXMGMT_FLAGS, flags))) goto nla_put_failure; genlmsg_end(msg, hdr); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 44341bf53cfc..2c0f2b3c07cb 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -66,7 +66,7 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u32 nlpid, int freq, int sig_dbm, - const u8 *buf, size_t len, gfp_t gfp); + const u8 *buf, size_t len, u32 flags, gfp_t gfp); void nl80211_radar_notify(struct cfg80211_registered_device *rdev, -- cgit v1.2.3-71-gd317 From f1bc1e4c44b1b78fe34431936c60759b5aad5e3f Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Fri, 23 Aug 2013 10:17:54 +0800 Subject: ata: acpi: rework the ata acpi bind support Binding ACPI handle to SCSI device has several drawbacks, namely: 1 During ATA device initialization time, ACPI handle will be needed while SCSI devices are not created yet. So each time ACPI handle is needed, instead of retrieving the handle by ACPI_HANDLE macro, a namespace scan is performed to find the handle for the corresponding ATA device. This is inefficient, and also expose a restriction on calling path not holding any lock. 2 The binding to SCSI device tree makes code complex, while at the same time doesn't bring us any benefit. All ACPI handlings are still done in ATA module, not in SCSI. Rework the ATA ACPI binding code to bind ACPI handle to ATA transport devices(ATA port and ATA device). The binding needs to be done only once, since the ATA transport devices do not go away with hotplug. And due to this, the flush_work call in hotplug handler for ATA bay is no longer needed. Tested on an Intel test platform for binding and runtime power off for ODD(ZPODD) and hard disk; on an ASUS S400C for binding and normal boot and S3, where its SATA port node has _SDD and _GTF control methods when configured as an AHCI controller and its PATA device node has _GTF control method when configured as an IDE controller. SATA PMP binding and ATA hotplug is not tested. Signed-off-by: Aaron Lu Tested-by: Dirk Griesbach Signed-off-by: Tejun Heo --- drivers/ata/libata-acpi.c | 278 ++++++++++++----------------------------- drivers/ata/libata-core.c | 5 - drivers/ata/libata-scsi.c | 13 +- drivers/ata/libata-transport.c | 2 + drivers/ata/libata-zpodd.c | 12 +- drivers/ata/libata.h | 16 ++- drivers/ata/pata_acpi.c | 4 +- drivers/scsi/scsi_lib.c | 22 ---- include/linux/libata.h | 2 - 9 files changed, 106 insertions(+), 248 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index cf4e7020adac..2377a32861dc 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -34,62 +34,25 @@ struct ata_acpi_gtf { u8 tf[REGS_PER_GTF]; /* regs. 0x1f1 - 0x1f7 */ } __packed; -/* - * Helper - belongs in the PCI layer somewhere eventually - */ -static int is_pci_dev(struct device *dev) -{ - return (dev->bus == &pci_bus_type); -} - static void ata_acpi_clear_gtf(struct ata_device *dev) { kfree(dev->gtf_cache); dev->gtf_cache = NULL; } -/** - * ata_ap_acpi_handle - provide the acpi_handle for an ata_port - * @ap: the acpi_handle returned will correspond to this port - * - * Returns the acpi_handle for the ACPI namespace object corresponding to - * the ata_port passed into the function, or NULL if no such object exists - */ -acpi_handle ata_ap_acpi_handle(struct ata_port *ap) -{ - if (ap->flags & ATA_FLAG_ACPI_SATA) - return NULL; - - return ap->scsi_host ? - DEVICE_ACPI_HANDLE(&ap->scsi_host->shost_gendev) : NULL; -} -EXPORT_SYMBOL(ata_ap_acpi_handle); - /** * ata_dev_acpi_handle - provide the acpi_handle for an ata_device - * @dev: the acpi_device returned will correspond to this port + * @dev: the acpi_handle returned will correspond to this device * * Returns the acpi_handle for the ACPI namespace object corresponding to * the ata_device passed into the function, or NULL if no such object exists + * or ACPI is disabled for this device due to consecutive errors. */ acpi_handle ata_dev_acpi_handle(struct ata_device *dev) { - acpi_integer adr; - struct ata_port *ap = dev->link->ap; - - if (libata_noacpi || dev->flags & ATA_DFLAG_ACPI_DISABLED) - return NULL; - - if (ap->flags & ATA_FLAG_ACPI_SATA) { - if (!sata_pmp_attached(ap)) - adr = SATA_ADR(ap->port_no, NO_PORT_MULT); - else - adr = SATA_ADR(ap->port_no, dev->link->pmp); - return acpi_get_child(DEVICE_ACPI_HANDLE(ap->host->dev), adr); - } else - return acpi_get_child(ata_ap_acpi_handle(ap), dev->devno); + return dev->flags & ATA_DFLAG_ACPI_DISABLED ? + NULL : ACPI_HANDLE(&dev->tdev); } -EXPORT_SYMBOL(ata_dev_acpi_handle); /* @ap and @dev are the same as ata_acpi_handle_hotplug() */ static void ata_acpi_detach_device(struct ata_port *ap, struct ata_device *dev) @@ -156,10 +119,8 @@ static void ata_acpi_handle_hotplug(struct ata_port *ap, struct ata_device *dev, spin_unlock_irqrestore(ap->lock, flags); - if (wait) { + if (wait) ata_port_wait_eh(ap); - flush_work(&ap->hotplug_task.work); - } } static void ata_acpi_dev_notify_dock(acpi_handle handle, u32 event, void *data) @@ -216,37 +177,55 @@ static const struct acpi_dock_ops ata_acpi_ap_dock_ops = { .uevent = ata_acpi_ap_uevent, }; -void ata_acpi_hotplug_init(struct ata_host *host) +/* bind acpi handle to pata port */ +void ata_acpi_bind_port(struct ata_port *ap) { - int i; + acpi_handle host_handle = ACPI_HANDLE(ap->host->dev); - for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap = host->ports[i]; - acpi_handle handle; - struct ata_device *dev; + if (libata_noacpi || ap->flags & ATA_FLAG_ACPI_SATA || !host_handle) + return; - if (!ap) - continue; + ACPI_HANDLE_SET(&ap->tdev, acpi_get_child(host_handle, ap->port_no)); - handle = ata_ap_acpi_handle(ap); - if (handle) { - /* we might be on a docking station */ - register_hotplug_dock_device(handle, - &ata_acpi_ap_dock_ops, ap, - NULL, NULL); - } + if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0) + ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; - ata_for_each_dev(dev, &ap->link, ALL) { - handle = ata_dev_acpi_handle(dev); - if (!handle) - continue; + /* we might be on a docking station */ + register_hotplug_dock_device(ACPI_HANDLE(&ap->tdev), + &ata_acpi_ap_dock_ops, ap, NULL, NULL); +} - /* we might be on a docking station */ - register_hotplug_dock_device(handle, - &ata_acpi_dev_dock_ops, - dev, NULL, NULL); - } +void ata_acpi_bind_dev(struct ata_device *dev) +{ + struct ata_port *ap = dev->link->ap; + acpi_handle port_handle = ACPI_HANDLE(&ap->tdev); + acpi_handle host_handle = ACPI_HANDLE(ap->host->dev); + acpi_handle parent_handle; + u64 adr; + + /* + * For both sata/pata devices, host handle is required. + * For pata device, port handle is also required. + */ + if (libata_noacpi || !host_handle || + (!(ap->flags & ATA_FLAG_ACPI_SATA) && !port_handle)) + return; + + if (ap->flags & ATA_FLAG_ACPI_SATA) { + if (!sata_pmp_attached(ap)) + adr = SATA_ADR(ap->port_no, NO_PORT_MULT); + else + adr = SATA_ADR(ap->port_no, dev->link->pmp); + parent_handle = host_handle; + } else { + adr = dev->devno; + parent_handle = port_handle; } + + ACPI_HANDLE_SET(&dev->tdev, acpi_get_child(parent_handle, adr)); + + register_hotplug_dock_device(ata_dev_acpi_handle(dev), + &ata_acpi_dev_dock_ops, dev, NULL, NULL); } /** @@ -270,18 +249,34 @@ void ata_acpi_dissociate(struct ata_host *host) struct ata_port *ap = host->ports[i]; const struct ata_acpi_gtm *gtm = ata_acpi_init_gtm(ap); - if (ata_ap_acpi_handle(ap) && gtm) + if (ACPI_HANDLE(&ap->tdev) && gtm) ata_acpi_stm(ap, gtm); } } -static int __ata_acpi_gtm(struct ata_port *ap, acpi_handle handle, - struct ata_acpi_gtm *gtm) +/** + * ata_acpi_gtm - execute _GTM + * @ap: target ATA port + * @gtm: out parameter for _GTM result + * + * Evaluate _GTM and store the result in @gtm. + * + * LOCKING: + * EH context. + * + * RETURNS: + * 0 on success, -ENOENT if _GTM doesn't exist, -errno on failure. + */ +int ata_acpi_gtm(struct ata_port *ap, struct ata_acpi_gtm *gtm) { struct acpi_buffer output = { .length = ACPI_ALLOCATE_BUFFER }; union acpi_object *out_obj; acpi_status status; int rc = 0; + acpi_handle handle = ACPI_HANDLE(&ap->tdev); + + if (!handle) + return -EINVAL; status = acpi_evaluate_object(handle, "_GTM", NULL, &output); @@ -317,27 +312,6 @@ static int __ata_acpi_gtm(struct ata_port *ap, acpi_handle handle, return rc; } -/** - * ata_acpi_gtm - execute _GTM - * @ap: target ATA port - * @gtm: out parameter for _GTM result - * - * Evaluate _GTM and store the result in @gtm. - * - * LOCKING: - * EH context. - * - * RETURNS: - * 0 on success, -ENOENT if _GTM doesn't exist, -errno on failure. - */ -int ata_acpi_gtm(struct ata_port *ap, struct ata_acpi_gtm *gtm) -{ - if (ata_ap_acpi_handle(ap)) - return __ata_acpi_gtm(ap, ata_ap_acpi_handle(ap), gtm); - else - return -EINVAL; -} - EXPORT_SYMBOL_GPL(ata_acpi_gtm); /** @@ -374,8 +348,8 @@ int ata_acpi_stm(struct ata_port *ap, const struct ata_acpi_gtm *stm) input.count = 3; input.pointer = in_params; - status = acpi_evaluate_object(ata_ap_acpi_handle(ap), "_STM", &input, - NULL); + status = acpi_evaluate_object(ACPI_HANDLE(&ap->tdev), "_STM", + &input, NULL); if (status == AE_NOT_FOUND) return -ENOENT; @@ -850,7 +824,7 @@ void ata_acpi_on_resume(struct ata_port *ap) const struct ata_acpi_gtm *gtm = ata_acpi_init_gtm(ap); struct ata_device *dev; - if (ata_ap_acpi_handle(ap) && gtm) { + if (ACPI_HANDLE(&ap->tdev) && gtm) { /* _GTM valid */ /* restore timing parameters */ @@ -894,8 +868,7 @@ static int ata_acpi_choose_suspend_state(struct ata_device *dev, bool runtime) d_max_in = ACPI_STATE_D3_HOT; out: - return acpi_pm_device_sleep_state(&dev->sdev->sdev_gendev, - NULL, d_max_in); + return acpi_pm_device_sleep_state(&dev->tdev, NULL, d_max_in); } static void sata_acpi_set_state(struct ata_port *ap, pm_message_t state) @@ -932,7 +905,7 @@ static void pata_acpi_set_state(struct ata_port *ap, pm_message_t state) struct ata_device *dev; acpi_handle port_handle; - port_handle = ata_ap_acpi_handle(ap); + port_handle = ACPI_HANDLE(&ap->tdev); if (!port_handle) return; @@ -1063,109 +1036,16 @@ void ata_acpi_on_disable(struct ata_device *dev) ata_acpi_clear_gtf(dev); } -static int compat_pci_ata(struct ata_port *ap) -{ - struct device *dev = ap->tdev.parent; - struct pci_dev *pdev; - - if (!is_pci_dev(dev)) - return 0; - - pdev = to_pci_dev(dev); - - if ((pdev->class >> 8) != PCI_CLASS_STORAGE_SATA && - (pdev->class >> 8) != PCI_CLASS_STORAGE_IDE) - return 0; - - return 1; -} - -static int ata_acpi_bind_host(struct ata_port *ap, acpi_handle *handle) -{ - if (libata_noacpi || ap->flags & ATA_FLAG_ACPI_SATA) - return -ENODEV; - - *handle = acpi_get_child(DEVICE_ACPI_HANDLE(ap->tdev.parent), - ap->port_no); - - if (!*handle) - return -ENODEV; - - if (__ata_acpi_gtm(ap, *handle, &ap->__acpi_init_gtm) == 0) - ap->pflags |= ATA_PFLAG_INIT_GTM_VALID; - - return 0; -} - -static int ata_acpi_bind_device(struct ata_port *ap, struct scsi_device *sdev, - acpi_handle *handle) -{ - struct ata_device *ata_dev; - - if (ap->flags & ATA_FLAG_ACPI_SATA) { - if (!sata_pmp_attached(ap)) - ata_dev = &ap->link.device[sdev->id]; - else - ata_dev = &ap->pmp_link[sdev->channel].device[sdev->id]; - } - else { - ata_dev = &ap->link.device[sdev->id]; - } - - *handle = ata_dev_acpi_handle(ata_dev); - - if (!*handle) - return -ENODEV; - - return 0; -} - -static int is_ata_port(const struct device *dev) -{ - return dev->type == &ata_port_type; -} - -static struct ata_port *dev_to_ata_port(struct device *dev) -{ - while (!is_ata_port(dev)) { - if (!dev->parent) - return NULL; - dev = dev->parent; - } - return to_ata_port(dev); -} - -static int ata_acpi_find_device(struct device *dev, acpi_handle *handle) -{ - struct ata_port *ap = dev_to_ata_port(dev); - - if (!ap) - return -ENODEV; - - if (!compat_pci_ata(ap)) - return -ENODEV; - - if (scsi_is_host_device(dev)) - return ata_acpi_bind_host(ap, handle); - else if (scsi_is_sdev_device(dev)) { - struct scsi_device *sdev = to_scsi_device(dev); - - return ata_acpi_bind_device(ap, sdev, handle); - } else - return -ENODEV; -} - -static struct acpi_bus_type ata_acpi_bus = { - .name = "ATA", - .find_device = ata_acpi_find_device, -}; - -int ata_acpi_register(void) +void ata_scsi_acpi_bind(struct ata_device *dev) { - return scsi_register_acpi_bus_type(&ata_acpi_bus); + acpi_handle handle = ata_dev_acpi_handle(dev); + if (handle) + acpi_dev_pm_add_dependent(handle, &dev->sdev->sdev_gendev); } -void ata_acpi_unregister(void) +void ata_scsi_acpi_unbind(struct ata_device *dev) { - scsi_unregister_acpi_bus_type(&ata_acpi_bus); + acpi_handle handle = ata_dev_acpi_handle(dev); + if (handle) + acpi_dev_pm_remove_dependent(handle, &dev->sdev->sdev_gendev); } diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c24354d44f3d..39e78e4524a3 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6150,8 +6150,6 @@ int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) if (rc) goto err_tadd; - ata_acpi_hotplug_init(host); - /* set cable, sata_spd_limit and report */ for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; @@ -6632,8 +6630,6 @@ static int __init ata_init(void) ata_parse_force_param(); - ata_acpi_register(); - rc = ata_sff_init(); if (rc) { kfree(ata_force_tbl); @@ -6660,7 +6656,6 @@ static void __exit ata_exit(void) ata_release_transport(ata_scsi_transport_template); libata_transport_exit(); ata_sff_exit(); - ata_acpi_unregister(); kfree(ata_force_tbl); } diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 83c08907e042..f177ad6bfd09 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -49,7 +49,6 @@ #include #include #include -#include #include #include "libata.h" @@ -3665,9 +3664,7 @@ void ata_scsi_scan_host(struct ata_port *ap, int sync) if (!IS_ERR(sdev)) { dev->sdev = sdev; scsi_device_put(sdev); - if (zpodd_dev_enabled(dev)) - dev_pm_qos_expose_flags( - &sdev->sdev_gendev, 0); + ata_scsi_acpi_bind(dev); } else { dev->sdev = NULL; } @@ -3755,6 +3752,8 @@ static void ata_scsi_remove_dev(struct ata_device *dev) struct scsi_device *sdev; unsigned long flags; + ata_scsi_acpi_unbind(dev); + /* Alas, we need to grab scan_mutex to ensure SCSI device * state doesn't change underneath us and thus * scsi_device_get() always succeeds. The mutex locking can @@ -3764,9 +3763,6 @@ static void ata_scsi_remove_dev(struct ata_device *dev) mutex_lock(&ap->scsi_host->scan_mutex); spin_lock_irqsave(ap->lock, flags); - if (zpodd_dev_enabled(dev)) - zpodd_exit(dev); - /* clearing dev->sdev is protected by host lock */ sdev = dev->sdev; dev->sdev = NULL; @@ -3816,6 +3812,9 @@ static void ata_scsi_handle_link_detach(struct ata_link *link) dev->flags &= ~ATA_DFLAG_DETACHED; spin_unlock_irqrestore(ap->lock, flags); + if (zpodd_dev_enabled(dev)) + zpodd_exit(dev); + ata_scsi_remove_dev(dev); } } diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index 077a856f5fd0..150a917f0c3c 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -287,6 +287,7 @@ int ata_tport_add(struct device *parent, dev->release = ata_tport_release; dev_set_name(dev, "ata%d", ap->print_id); transport_setup_device(dev); + ata_acpi_bind_port(ap); error = device_add(dev); if (error) { goto tport_err; @@ -644,6 +645,7 @@ static int ata_tdev_add(struct ata_device *ata_dev) dev_set_name(dev, "dev%d.%d.0", ap->print_id, link->pmp); transport_setup_device(dev); + ata_acpi_bind_dev(ata_dev); error = device_add(dev); if (error) { ata_tdev_free(ata_dev); diff --git a/drivers/ata/libata-zpodd.c b/drivers/ata/libata-zpodd.c index cd8daf47188b..68f9e3293e9c 100644 --- a/drivers/ata/libata-zpodd.c +++ b/drivers/ata/libata-zpodd.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "libata.h" @@ -190,8 +191,8 @@ void zpodd_enable_run_wake(struct ata_device *dev) sdev_disable_disk_events(dev->sdev); zpodd->powered_off = true; - device_set_run_wake(&dev->sdev->sdev_gendev, true); - acpi_pm_device_run_wake(&dev->sdev->sdev_gendev, true); + device_set_run_wake(&dev->tdev, true); + acpi_pm_device_run_wake(&dev->tdev, true); } /* Disable runtime wake capability if it is enabled */ @@ -200,8 +201,8 @@ void zpodd_disable_run_wake(struct ata_device *dev) struct zpodd *zpodd = dev->zpodd; if (zpodd->powered_off) { - acpi_pm_device_run_wake(&dev->sdev->sdev_gendev, false); - device_set_run_wake(&dev->sdev->sdev_gendev, false); + acpi_pm_device_run_wake(&dev->tdev, false); + device_set_run_wake(&dev->tdev, false); } } @@ -262,7 +263,7 @@ static void ata_acpi_add_pm_notifier(struct ata_device *dev) static void ata_acpi_remove_pm_notifier(struct ata_device *dev) { - acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->sdev->sdev_gendev); + acpi_handle handle = ata_dev_acpi_handle(dev); acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, zpodd_wake_dev); } @@ -290,6 +291,7 @@ void zpodd_init(struct ata_device *dev) ata_acpi_add_pm_notifier(dev); zpodd->dev = dev; dev->zpodd = zpodd; + dev_pm_qos_expose_flags(&dev->tdev, 0); } void zpodd_exit(struct ata_device *dev) diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index cc8a6e879dfb..3e9cc95a0222 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -118,9 +118,11 @@ extern void ata_acpi_on_resume(struct ata_port *ap); extern int ata_acpi_on_devcfg(struct ata_device *dev); extern void ata_acpi_on_disable(struct ata_device *dev); extern void ata_acpi_set_state(struct ata_port *ap, pm_message_t state); -extern int ata_acpi_register(void); -extern void ata_acpi_unregister(void); -extern void ata_acpi_hotplug_init(struct ata_host *host); +extern void ata_acpi_bind_port(struct ata_port *ap); +extern void ata_acpi_bind_dev(struct ata_device *dev); +extern acpi_handle ata_dev_acpi_handle(struct ata_device *dev); +extern void ata_scsi_acpi_bind(struct ata_device *dev); +extern void ata_scsi_acpi_unbind(struct ata_device *dev); #else static inline void ata_acpi_dissociate(struct ata_host *host) { } static inline int ata_acpi_on_suspend(struct ata_port *ap) { return 0; } @@ -129,9 +131,11 @@ static inline int ata_acpi_on_devcfg(struct ata_device *dev) { return 0; } static inline void ata_acpi_on_disable(struct ata_device *dev) { } static inline void ata_acpi_set_state(struct ata_port *ap, pm_message_t state) { } -static inline int ata_acpi_register(void) { return 0; } -static inline void ata_acpi_unregister(void) { } -static inline void ata_acpi_hotplug_init(struct ata_host *host) {} +static inline void ata_acpi_bind_port(struct ata_port *ap) {} +static inline void ata_acpi_bind_dev(struct ata_device *dev) {} +static inline acpi_handle ata_dev_acpi_handle(struct ata_device *dev) { return NULL; } +static inline void ata_scsi_acpi_bind(struct ata_device *dev) {} +static inline void ata_scsi_acpi_unbind(struct ata_device *dev) {} #endif /* libata-scsi.c */ diff --git a/drivers/ata/pata_acpi.c b/drivers/ata/pata_acpi.c index 09723b76beac..73212c9c6d5b 100644 --- a/drivers/ata/pata_acpi.c +++ b/drivers/ata/pata_acpi.c @@ -39,7 +39,7 @@ static int pacpi_pre_reset(struct ata_link *link, unsigned long deadline) { struct ata_port *ap = link->ap; struct pata_acpi *acpi = ap->private_data; - if (ata_ap_acpi_handle(ap) == NULL || ata_acpi_gtm(ap, &acpi->gtm) < 0) + if (ACPI_HANDLE(&ap->tdev) == NULL || ata_acpi_gtm(ap, &acpi->gtm) < 0) return -ENODEV; return ata_sff_prereset(link, deadline); @@ -195,7 +195,7 @@ static int pacpi_port_start(struct ata_port *ap) struct pci_dev *pdev = to_pci_dev(ap->host->dev); struct pata_acpi *acpi; - if (ata_ap_acpi_handle(ap) == NULL) + if (ACPI_HANDLE(&ap->tdev) == NULL) return -ENODEV; acpi = ap->private_data = devm_kzalloc(&pdev->dev, sizeof(struct pata_acpi), GFP_KERNEL); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 124392f3091e..f0ddc7dd06d3 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -68,28 +68,6 @@ static struct scsi_host_sg_pool scsi_sg_pools[] = { struct kmem_cache *scsi_sdb_cache; -#ifdef CONFIG_ACPI -#include - -static bool acpi_scsi_bus_match(struct device *dev) -{ - return dev->bus == &scsi_bus_type; -} - -int scsi_register_acpi_bus_type(struct acpi_bus_type *bus) -{ - bus->match = acpi_scsi_bus_match; - return register_acpi_bus_type(bus); -} -EXPORT_SYMBOL_GPL(scsi_register_acpi_bus_type); - -void scsi_unregister_acpi_bus_type(struct acpi_bus_type *bus) -{ - unregister_acpi_bus_type(bus); -} -EXPORT_SYMBOL_GPL(scsi_unregister_acpi_bus_type); -#endif - /* * When to reinvoke queueing after a resource shortage. It's 3 msecs to * not change behaviour from the previous unplug mechanism, experimentation diff --git a/include/linux/libata.h b/include/linux/libata.h index 283d66bc603c..3198617905e1 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1244,8 +1244,6 @@ int ata_acpi_stm(struct ata_port *ap, const struct ata_acpi_gtm *stm); int ata_acpi_gtm(struct ata_port *ap, struct ata_acpi_gtm *stm); unsigned long ata_acpi_gtm_xfermask(struct ata_device *dev, const struct ata_acpi_gtm *gtm); -acpi_handle ata_ap_acpi_handle(struct ata_port *ap); -acpi_handle ata_dev_acpi_handle(struct ata_device *dev); int ata_acpi_cbl_80wire(struct ata_port *ap, const struct ata_acpi_gtm *gtm); #else static inline const struct ata_acpi_gtm *ata_acpi_init_gtm(struct ata_port *ap) -- cgit v1.2.3-71-gd317 From 87f14e658f664a17dcdb38d3f6ae70642fcdc263 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 1 Jul 2013 15:16:24 +0200 Subject: [SCSI] Set hostbyte status in scsi_check_sense() We should be modifying the host_byte status in scsi_check_sense() directly; this saves us to introduce a special return code for each and every condition. Signed-off-by: Hannes Reinecke Signed-off-by: James Bottomley --- drivers/scsi/scsi_error.c | 21 +++++++-------------- include/scsi/scsi.h | 1 - 2 files changed, 7 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 21505962f539..7be5229ed3a4 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -228,7 +228,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost, * @scmd: Cmd to have sense checked. * * Return value: - * SUCCESS or FAILED or NEEDS_RETRY or TARGET_ERROR + * SUCCESS or FAILED or NEEDS_RETRY or ADD_TO_MLQUEUE * * Notes: * When a deferred error is detected the current command has @@ -359,13 +359,15 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) case MISCOMPARE: case BLANK_CHECK: case DATA_PROTECT: - return TARGET_ERROR; + set_host_byte(scmd, DID_TARGET_FAILURE); + return SUCCESS; case MEDIUM_ERROR: if (sshdr.asc == 0x11 || /* UNRECOVERED READ ERR */ sshdr.asc == 0x13 || /* AMNF DATA FIELD */ sshdr.asc == 0x14) { /* RECORD NOT FOUND */ - return TARGET_ERROR; + set_host_byte(scmd, DID_TARGET_FAILURE); + return SUCCESS; } return NEEDS_RETRY; @@ -373,14 +375,14 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) if (scmd->device->retry_hwerror) return ADD_TO_MLQUEUE; else - return TARGET_ERROR; + set_host_byte(scmd, DID_TARGET_FAILURE); case ILLEGAL_REQUEST: if (sshdr.asc == 0x20 || /* Invalid command operation code */ sshdr.asc == 0x21 || /* Logical block address out of range */ sshdr.asc == 0x24 || /* Invalid field in cdb */ sshdr.asc == 0x26) { /* Parameter value invalid */ - return TARGET_ERROR; + set_host_byte(scmd, DID_TARGET_FAILURE); } return SUCCESS; @@ -843,7 +845,6 @@ retry: case SUCCESS: case NEEDS_RETRY: case FAILED: - case TARGET_ERROR: break; case ADD_TO_MLQUEUE: rtn = NEEDS_RETRY; @@ -1577,14 +1578,6 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) rtn = scsi_check_sense(scmd); if (rtn == NEEDS_RETRY) goto maybe_retry; - else if (rtn == TARGET_ERROR) { - /* - * Need to modify host byte to signal a - * permanent target failure - */ - set_host_byte(scmd, DID_TARGET_FAILURE); - rtn = SUCCESS; - } /* if rtn == FAILED, we have no sense information; * returning FAILED will wake the error handler thread * to collect the sense and redo the decide diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 4b87d99e7fa1..e4cd3e975538 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -486,7 +486,6 @@ static inline int scsi_is_wlun(unsigned int lun) #define TIMEOUT_ERROR 0x2007 #define SCSI_RETURN_NOT_HANDLED 0x2008 #define FAST_IO_FAIL 0x2009 -#define TARGET_ERROR 0x200A /* * Midlevel queue return values. -- cgit v1.2.3-71-gd317 From a9d6ceb838755c24dde8a0ca02c3378926fc63db Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 1 Jul 2013 15:16:25 +0200 Subject: [SCSI] return ENOSPC on thin provisioning failure When the thin provisioning hard threshold is reached we should return ENOSPC to inform upper layers about this fact. Signed-off-by: Hannes Reinecke Signed-off-by: James Bottomley --- block/blk-core.c | 3 +++ drivers/scsi/scsi_error.c | 7 ++++++- drivers/scsi/scsi_lib.c | 5 +++++ include/scsi/scsi.h | 1 + 4 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 93a18d1d3da8..68ce4d53a528 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2318,6 +2318,9 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes) case -ETIMEDOUT: error_type = "timeout"; break; + case -ENOSPC: + error_type = "critical space allocation"; + break; case -EIO: default: error_type = "I/O"; diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 7be5229ed3a4..1b1298ce0e71 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -354,11 +354,16 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) return SUCCESS; /* these are not supported */ + case DATA_PROTECT: + if (sshdr.asc == 0x27 && sshdr.ascq == 0x07) { + /* Thin provisioning hard threshold reached */ + set_host_byte(scmd, DID_ALLOC_FAILURE); + return SUCCESS; + } case COPY_ABORTED: case VOLUME_OVERFLOW: case MISCOMPARE: case BLANK_CHECK: - case DATA_PROTECT: set_host_byte(scmd, DID_TARGET_FAILURE); return SUCCESS; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index e2af8ae0d0ca..49020d52d685 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -726,6 +726,7 @@ EXPORT_SYMBOL(scsi_release_buffers); * -ENOLINK temporary transport failure * -EREMOTEIO permanent target failure, do not retry * -EBADE permanent nexus failure, retry on other path + * -ENOSPC No write space available * -EIO unspecified I/O error */ static int __scsi_error_from_host_byte(struct scsi_cmnd *cmd, int result) @@ -744,6 +745,10 @@ static int __scsi_error_from_host_byte(struct scsi_cmnd *cmd, int result) set_host_byte(cmd, DID_OK); error = -EBADE; break; + case DID_ALLOC_FAILURE: + set_host_byte(cmd, DID_OK); + error = -ENOSPC; + break; default: error = -EIO; break; diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index e4cd3e975538..d0387d84dd68 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -457,6 +457,7 @@ static inline int scsi_is_wlun(unsigned int lun) * other paths */ #define DID_NEXUS_FAILURE 0x11 /* Permanent nexus failure, retry on other * paths might yield different results */ +#define DID_ALLOC_FAILURE 0x12 /* Space allocation on the device failed */ #define DRIVER_OK 0x00 /* Driver status */ /* -- cgit v1.2.3-71-gd317 From 7e782af57649f8a8e943d80104c946a5cd7af7cc Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 1 Jul 2013 15:16:26 +0200 Subject: [SCSI] Return ENODATA on medium error When a medium error is detected the SCSI stack should return ENODATA to the upper layers. [jejb: fix whitespace error] Signed-off-by: Hannes Reinecke Signed-off-by: James Bottomley --- block/blk-core.c | 3 +++ drivers/md/dm-mpath.c | 16 +++++++++++++++- drivers/scsi/scsi_error.c | 2 +- drivers/scsi/scsi_lib.c | 5 +++++ include/scsi/scsi.h | 1 + 5 files changed, 25 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 68ce4d53a528..c04505358342 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2321,6 +2321,9 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes) case -ENOSPC: error_type = "critical space allocation"; break; + case -ENODATA: + error_type = "critical medium"; + break; case -EIO: default: error_type = "I/O"; diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 5adede17ddf6..b759a127f9c3 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1261,6 +1261,20 @@ static void activate_path(struct work_struct *work) pg_init_done, pgpath); } +static int noretry_error(int error) +{ + switch (error) { + case -EOPNOTSUPP: + case -EREMOTEIO: + case -EILSEQ: + case -ENODATA: + return 1; + } + + /* Anything else could be a path failure, so should be retried */ + return 0; +} + /* * end_io handling */ @@ -1284,7 +1298,7 @@ static int do_end_io(struct multipath *m, struct request *clone, if (!error && !clone->errors) return 0; /* I/O complete */ - if (error == -EOPNOTSUPP || error == -EREMOTEIO || error == -EILSEQ) + if (noretry_error(error)) return error; if (mpio->pgpath) diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 1b1298ce0e71..a46c3dddcf70 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -371,7 +371,7 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) if (sshdr.asc == 0x11 || /* UNRECOVERED READ ERR */ sshdr.asc == 0x13 || /* AMNF DATA FIELD */ sshdr.asc == 0x14) { /* RECORD NOT FOUND */ - set_host_byte(scmd, DID_TARGET_FAILURE); + set_host_byte(scmd, DID_MEDIUM_ERROR); return SUCCESS; } return NEEDS_RETRY; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 49020d52d685..36b5c898db9d 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -727,6 +727,7 @@ EXPORT_SYMBOL(scsi_release_buffers); * -EREMOTEIO permanent target failure, do not retry * -EBADE permanent nexus failure, retry on other path * -ENOSPC No write space available + * -ENODATA Medium error * -EIO unspecified I/O error */ static int __scsi_error_from_host_byte(struct scsi_cmnd *cmd, int result) @@ -749,6 +750,10 @@ static int __scsi_error_from_host_byte(struct scsi_cmnd *cmd, int result) set_host_byte(cmd, DID_OK); error = -ENOSPC; break; + case DID_MEDIUM_ERROR: + set_host_byte(cmd, DID_OK); + error = -ENODATA; + break; default: error = -EIO; break; diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index d0387d84dd68..d477bfb73fb9 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -458,6 +458,7 @@ static inline int scsi_is_wlun(unsigned int lun) #define DID_NEXUS_FAILURE 0x11 /* Permanent nexus failure, retry on other * paths might yield different results */ #define DID_ALLOC_FAILURE 0x12 /* Space allocation on the device failed */ +#define DID_MEDIUM_ERROR 0x13 /* Medium error */ #define DRIVER_OK 0x00 /* Driver status */ /* -- cgit v1.2.3-71-gd317 From ae542edb11c79706cd74d7bd54ebd7702965a7f3 Mon Sep 17 00:00:00 2001 From: Adheer Chandravanshi Date: Mon, 1 Jul 2013 05:54:11 -0400 Subject: [SCSI] scsi_transport_iscsi: Exporting new attrs for iscsi session and connection in sysfs Signed-off-by: Adheer Chandravanshi Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_iscsi.c | 123 +++++++++++++++++++++++++++++++++++- include/scsi/iscsi_if.h | 32 ++++++++++ 2 files changed, 154 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index abf7c402e1a5..e4a989fa477d 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -3327,6 +3326,23 @@ iscsi_conn_attr(exp_statsn, ISCSI_PARAM_EXP_STATSN); iscsi_conn_attr(persistent_address, ISCSI_PARAM_PERSISTENT_ADDRESS); iscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO); iscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO); +iscsi_conn_attr(local_port, ISCSI_PARAM_LOCAL_PORT); +iscsi_conn_attr(statsn, ISCSI_PARAM_STATSN); +iscsi_conn_attr(keepalive_tmo, ISCSI_PARAM_KEEPALIVE_TMO); +iscsi_conn_attr(max_segment_size, ISCSI_PARAM_MAX_SEGMENT_SIZE); +iscsi_conn_attr(tcp_timestamp_stat, ISCSI_PARAM_TCP_TIMESTAMP_STAT); +iscsi_conn_attr(tcp_wsf_disable, ISCSI_PARAM_TCP_WSF_DISABLE); +iscsi_conn_attr(tcp_nagle_disable, ISCSI_PARAM_TCP_NAGLE_DISABLE); +iscsi_conn_attr(tcp_timer_scale, ISCSI_PARAM_TCP_TIMER_SCALE); +iscsi_conn_attr(tcp_timestamp_enable, ISCSI_PARAM_TCP_TIMESTAMP_EN); +iscsi_conn_attr(fragment_disable, ISCSI_PARAM_IP_FRAGMENT_DISABLE); +iscsi_conn_attr(ipv4_tos, ISCSI_PARAM_IPV4_TOS); +iscsi_conn_attr(ipv6_traffic_class, ISCSI_PARAM_IPV6_TC); +iscsi_conn_attr(ipv6_flow_label, ISCSI_PARAM_IPV6_FLOW_LABEL); +iscsi_conn_attr(is_fw_assigned_ipv6, ISCSI_PARAM_IS_FW_ASSIGNED_IPV6); +iscsi_conn_attr(tcp_xmit_wsf, ISCSI_PARAM_TCP_XMIT_WSF); +iscsi_conn_attr(tcp_recv_wsf, ISCSI_PARAM_TCP_RECV_WSF); + #define iscsi_conn_ep_attr_show(param) \ static ssize_t show_conn_ep_param_##param(struct device *dev, \ @@ -3379,6 +3395,22 @@ static struct attribute *iscsi_conn_attrs[] = { &dev_attr_conn_persistent_port.attr, &dev_attr_conn_ping_tmo.attr, &dev_attr_conn_recv_tmo.attr, + &dev_attr_conn_local_port.attr, + &dev_attr_conn_statsn.attr, + &dev_attr_conn_keepalive_tmo.attr, + &dev_attr_conn_max_segment_size.attr, + &dev_attr_conn_tcp_timestamp_stat.attr, + &dev_attr_conn_tcp_wsf_disable.attr, + &dev_attr_conn_tcp_nagle_disable.attr, + &dev_attr_conn_tcp_timer_scale.attr, + &dev_attr_conn_tcp_timestamp_enable.attr, + &dev_attr_conn_fragment_disable.attr, + &dev_attr_conn_ipv4_tos.attr, + &dev_attr_conn_ipv6_traffic_class.attr, + &dev_attr_conn_ipv6_flow_label.attr, + &dev_attr_conn_is_fw_assigned_ipv6.attr, + &dev_attr_conn_tcp_xmit_wsf.attr, + &dev_attr_conn_tcp_recv_wsf.attr, NULL, }; @@ -3416,6 +3448,38 @@ static umode_t iscsi_conn_attr_is_visible(struct kobject *kobj, param = ISCSI_PARAM_PING_TMO; else if (attr == &dev_attr_conn_recv_tmo.attr) param = ISCSI_PARAM_RECV_TMO; + else if (attr == &dev_attr_conn_local_port.attr) + param = ISCSI_PARAM_LOCAL_PORT; + else if (attr == &dev_attr_conn_statsn.attr) + param = ISCSI_PARAM_STATSN; + else if (attr == &dev_attr_conn_keepalive_tmo.attr) + param = ISCSI_PARAM_KEEPALIVE_TMO; + else if (attr == &dev_attr_conn_max_segment_size.attr) + param = ISCSI_PARAM_MAX_SEGMENT_SIZE; + else if (attr == &dev_attr_conn_tcp_timestamp_stat.attr) + param = ISCSI_PARAM_TCP_TIMESTAMP_STAT; + else if (attr == &dev_attr_conn_tcp_wsf_disable.attr) + param = ISCSI_PARAM_TCP_WSF_DISABLE; + else if (attr == &dev_attr_conn_tcp_nagle_disable.attr) + param = ISCSI_PARAM_TCP_NAGLE_DISABLE; + else if (attr == &dev_attr_conn_tcp_timer_scale.attr) + param = ISCSI_PARAM_TCP_TIMER_SCALE; + else if (attr == &dev_attr_conn_tcp_timestamp_enable.attr) + param = ISCSI_PARAM_TCP_TIMESTAMP_EN; + else if (attr == &dev_attr_conn_fragment_disable.attr) + param = ISCSI_PARAM_IP_FRAGMENT_DISABLE; + else if (attr == &dev_attr_conn_ipv4_tos.attr) + param = ISCSI_PARAM_IPV4_TOS; + else if (attr == &dev_attr_conn_ipv6_traffic_class.attr) + param = ISCSI_PARAM_IPV6_TC; + else if (attr == &dev_attr_conn_ipv6_flow_label.attr) + param = ISCSI_PARAM_IPV6_FLOW_LABEL; + else if (attr == &dev_attr_conn_is_fw_assigned_ipv6.attr) + param = ISCSI_PARAM_IS_FW_ASSIGNED_IPV6; + else if (attr == &dev_attr_conn_tcp_xmit_wsf.attr) + param = ISCSI_PARAM_TCP_XMIT_WSF; + else if (attr == &dev_attr_conn_tcp_recv_wsf.attr) + param = ISCSI_PARAM_TCP_RECV_WSF; else { WARN_ONCE(1, "Invalid conn attr"); return 0; @@ -3476,6 +3540,21 @@ iscsi_session_attr(targetalias, ISCSI_PARAM_TARGET_ALIAS, 0); iscsi_session_attr(boot_root, ISCSI_PARAM_BOOT_ROOT, 0); iscsi_session_attr(boot_nic, ISCSI_PARAM_BOOT_NIC, 0); iscsi_session_attr(boot_target, ISCSI_PARAM_BOOT_TARGET, 0); +iscsi_session_attr(auto_snd_tgt_disable, ISCSI_PARAM_AUTO_SND_TGT_DISABLE, 0); +iscsi_session_attr(discovery_session, ISCSI_PARAM_DISCOVERY_SESS, 0); +iscsi_session_attr(portal_type, ISCSI_PARAM_PORTAL_TYPE, 0); +iscsi_session_attr(chap_auth, ISCSI_PARAM_CHAP_AUTH_EN, 0); +iscsi_session_attr(discovery_logout, ISCSI_PARAM_DISCOVERY_LOGOUT_EN, 0); +iscsi_session_attr(bidi_chap, ISCSI_PARAM_BIDI_CHAP_EN, 0); +iscsi_session_attr(discovery_auth_optional, + ISCSI_PARAM_DISCOVERY_AUTH_OPTIONAL, 0); +iscsi_session_attr(def_time2wait, ISCSI_PARAM_DEF_TIME2WAIT, 0); +iscsi_session_attr(def_time2retain, ISCSI_PARAM_DEF_TIME2RETAIN, 0); +iscsi_session_attr(isid, ISCSI_PARAM_ISID, 0); +iscsi_session_attr(tsid, ISCSI_PARAM_TSID, 0); +iscsi_session_attr(def_taskmgmt_tmo, ISCSI_PARAM_DEF_TASKMGMT_TMO, 0); +iscsi_session_attr(discovery_parent_idx, ISCSI_PARAM_DISCOVERY_PARENT_IDX, 0); +iscsi_session_attr(discovery_parent_type, ISCSI_PARAM_DISCOVERY_PARENT_TYPE, 0); static ssize_t show_priv_session_state(struct device *dev, struct device_attribute *attr, @@ -3580,6 +3659,20 @@ static struct attribute *iscsi_session_attrs[] = { &dev_attr_sess_chap_out_idx.attr, &dev_attr_sess_chap_in_idx.attr, &dev_attr_priv_sess_target_id.attr, + &dev_attr_sess_auto_snd_tgt_disable.attr, + &dev_attr_sess_discovery_session.attr, + &dev_attr_sess_portal_type.attr, + &dev_attr_sess_chap_auth.attr, + &dev_attr_sess_discovery_logout.attr, + &dev_attr_sess_bidi_chap.attr, + &dev_attr_sess_discovery_auth_optional.attr, + &dev_attr_sess_def_time2wait.attr, + &dev_attr_sess_def_time2retain.attr, + &dev_attr_sess_isid.attr, + &dev_attr_sess_tsid.attr, + &dev_attr_sess_def_taskmgmt_tmo.attr, + &dev_attr_sess_discovery_parent_idx.attr, + &dev_attr_sess_discovery_parent_type.attr, NULL, }; @@ -3643,6 +3736,34 @@ static umode_t iscsi_session_attr_is_visible(struct kobject *kobj, param = ISCSI_PARAM_BOOT_NIC; else if (attr == &dev_attr_sess_boot_target.attr) param = ISCSI_PARAM_BOOT_TARGET; + else if (attr == &dev_attr_sess_auto_snd_tgt_disable.attr) + param = ISCSI_PARAM_AUTO_SND_TGT_DISABLE; + else if (attr == &dev_attr_sess_discovery_session.attr) + param = ISCSI_PARAM_DISCOVERY_SESS; + else if (attr == &dev_attr_sess_portal_type.attr) + param = ISCSI_PARAM_PORTAL_TYPE; + else if (attr == &dev_attr_sess_chap_auth.attr) + param = ISCSI_PARAM_CHAP_AUTH_EN; + else if (attr == &dev_attr_sess_discovery_logout.attr) + param = ISCSI_PARAM_DISCOVERY_LOGOUT_EN; + else if (attr == &dev_attr_sess_bidi_chap.attr) + param = ISCSI_PARAM_BIDI_CHAP_EN; + else if (attr == &dev_attr_sess_discovery_auth_optional.attr) + param = ISCSI_PARAM_DISCOVERY_AUTH_OPTIONAL; + else if (attr == &dev_attr_sess_def_time2wait.attr) + param = ISCSI_PARAM_DEF_TIME2WAIT; + else if (attr == &dev_attr_sess_def_time2retain.attr) + param = ISCSI_PARAM_DEF_TIME2RETAIN; + else if (attr == &dev_attr_sess_isid.attr) + param = ISCSI_PARAM_ISID; + else if (attr == &dev_attr_sess_tsid.attr) + param = ISCSI_PARAM_TSID; + else if (attr == &dev_attr_sess_def_taskmgmt_tmo.attr) + param = ISCSI_PARAM_DEF_TASKMGMT_TMO; + else if (attr == &dev_attr_sess_discovery_parent_idx.attr) + param = ISCSI_PARAM_DISCOVERY_PARENT_IDX; + else if (attr == &dev_attr_sess_discovery_parent_type.attr) + param = ISCSI_PARAM_DISCOVERY_PARENT_TYPE; else if (attr == &dev_attr_priv_sess_recovery_tmo.attr) return S_IRUGO | S_IWUSR; else if (attr == &dev_attr_priv_sess_state.attr) diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index 9d28ded2a3f4..13d81c5c4ebf 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -494,6 +494,38 @@ enum iscsi_param { ISCSI_PARAM_BOOT_NIC, ISCSI_PARAM_BOOT_TARGET, + ISCSI_PARAM_AUTO_SND_TGT_DISABLE, + ISCSI_PARAM_DISCOVERY_SESS, + ISCSI_PARAM_PORTAL_TYPE, + ISCSI_PARAM_CHAP_AUTH_EN, + ISCSI_PARAM_DISCOVERY_LOGOUT_EN, + ISCSI_PARAM_BIDI_CHAP_EN, + ISCSI_PARAM_DISCOVERY_AUTH_OPTIONAL, + + ISCSI_PARAM_DEF_TIME2WAIT, + ISCSI_PARAM_DEF_TIME2RETAIN, + ISCSI_PARAM_MAX_SEGMENT_SIZE, + ISCSI_PARAM_STATSN, + ISCSI_PARAM_KEEPALIVE_TMO, + ISCSI_PARAM_LOCAL_PORT, + ISCSI_PARAM_TSID, + ISCSI_PARAM_DEF_TASKMGMT_TMO, + + ISCSI_PARAM_TCP_TIMESTAMP_STAT, + ISCSI_PARAM_TCP_WSF_DISABLE, + ISCSI_PARAM_TCP_NAGLE_DISABLE, + ISCSI_PARAM_TCP_TIMER_SCALE, + ISCSI_PARAM_TCP_TIMESTAMP_EN, + ISCSI_PARAM_TCP_XMIT_WSF, + ISCSI_PARAM_TCP_RECV_WSF, + ISCSI_PARAM_IP_FRAGMENT_DISABLE, + ISCSI_PARAM_IPV4_TOS, + ISCSI_PARAM_IPV6_TC, + ISCSI_PARAM_IPV6_FLOW_LABEL, + ISCSI_PARAM_IS_FW_ASSIGNED_IPV6, + + ISCSI_PARAM_DISCOVERY_PARENT_IDX, + ISCSI_PARAM_DISCOVERY_PARENT_TYPE, /* must always be last */ ISCSI_PARAM_MAX, }; -- cgit v1.2.3-71-gd317 From f8525eb4cff0c5df5b513a5f30ffebcda43460bb Mon Sep 17 00:00:00 2001 From: Adheer Chandravanshi Date: Mon, 1 Jul 2013 05:54:12 -0400 Subject: [SCSI] libiscsi: Exporting new attrs for iscsi session and connection in sysfs Signed-off-by: Adheer Chandravanshi Signed-off-by: Vikas Chaudhary Reviewed-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/libiscsi.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ include/scsi/libiscsi.h | 31 ++++++++++++++++ 2 files changed, 128 insertions(+) (limited to 'include') diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index ae69dfcc7834..86153e087e81 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -2812,6 +2812,8 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) kfree(session->boot_nic); kfree(session->boot_target); kfree(session->ifacename); + kfree(session->portal_type); + kfree(session->discovery_parent_type); iscsi_destroy_session(cls_session); iscsi_host_dec_session_cnt(shost); @@ -3257,6 +3259,11 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, return iscsi_switch_str_param(&session->boot_nic, buf); case ISCSI_PARAM_BOOT_TARGET: return iscsi_switch_str_param(&session->boot_target, buf); + case ISCSI_PARAM_PORTAL_TYPE: + return iscsi_switch_str_param(&session->portal_type, buf); + case ISCSI_PARAM_DISCOVERY_PARENT_TYPE: + return iscsi_switch_str_param(&session->discovery_parent_type, + buf); default: return -ENOSYS; } @@ -3343,6 +3350,51 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session, break; case ISCSI_PARAM_BOOT_TARGET: len = sprintf(buf, "%s\n", session->boot_target); + case ISCSI_PARAM_AUTO_SND_TGT_DISABLE: + len = sprintf(buf, "%u\n", session->auto_snd_tgt_disable); + break; + case ISCSI_PARAM_DISCOVERY_SESS: + len = sprintf(buf, "%u\n", session->discovery_sess); + break; + case ISCSI_PARAM_PORTAL_TYPE: + len = sprintf(buf, "%s\n", session->portal_type); + break; + case ISCSI_PARAM_CHAP_AUTH_EN: + len = sprintf(buf, "%u\n", session->chap_auth_en); + break; + case ISCSI_PARAM_DISCOVERY_LOGOUT_EN: + len = sprintf(buf, "%u\n", session->discovery_logout_en); + break; + case ISCSI_PARAM_BIDI_CHAP_EN: + len = sprintf(buf, "%u\n", session->bidi_chap_en); + break; + case ISCSI_PARAM_DISCOVERY_AUTH_OPTIONAL: + len = sprintf(buf, "%u\n", session->discovery_auth_optional); + break; + case ISCSI_PARAM_DEF_TIME2WAIT: + len = sprintf(buf, "%d\n", session->time2wait); + break; + case ISCSI_PARAM_DEF_TIME2RETAIN: + len = sprintf(buf, "%d\n", session->time2retain); + break; + case ISCSI_PARAM_TSID: + len = sprintf(buf, "%u\n", session->tsid); + break; + case ISCSI_PARAM_ISID: + len = sprintf(buf, "%02x%02x%02x%02x%02x%02x\n", + session->isid[0], session->isid[1], + session->isid[2], session->isid[3], + session->isid[4], session->isid[5]); + break; + case ISCSI_PARAM_DISCOVERY_PARENT_IDX: + len = sprintf(buf, "%u\n", session->discovery_parent_idx); + break; + case ISCSI_PARAM_DISCOVERY_PARENT_TYPE: + if (session->discovery_parent_type) + len = sprintf(buf, "%s\n", + session->discovery_parent_type); + else + len = sprintf(buf, "\n"); break; default: return -ENOSYS; @@ -3433,6 +3485,51 @@ int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, case ISCSI_PARAM_PERSISTENT_ADDRESS: len = sprintf(buf, "%s\n", conn->persistent_address); break; + case ISCSI_PARAM_STATSN: + len = sprintf(buf, "%u\n", conn->statsn); + break; + case ISCSI_PARAM_MAX_SEGMENT_SIZE: + len = sprintf(buf, "%u\n", conn->max_segment_size); + break; + case ISCSI_PARAM_KEEPALIVE_TMO: + len = sprintf(buf, "%u\n", conn->keepalive_tmo); + break; + case ISCSI_PARAM_LOCAL_PORT: + len = sprintf(buf, "%u\n", conn->local_port); + break; + case ISCSI_PARAM_TCP_TIMESTAMP_STAT: + len = sprintf(buf, "%u\n", conn->tcp_timestamp_stat); + break; + case ISCSI_PARAM_TCP_NAGLE_DISABLE: + len = sprintf(buf, "%u\n", conn->tcp_nagle_disable); + break; + case ISCSI_PARAM_TCP_WSF_DISABLE: + len = sprintf(buf, "%u\n", conn->tcp_wsf_disable); + break; + case ISCSI_PARAM_TCP_TIMER_SCALE: + len = sprintf(buf, "%u\n", conn->tcp_timer_scale); + break; + case ISCSI_PARAM_TCP_TIMESTAMP_EN: + len = sprintf(buf, "%u\n", conn->tcp_timestamp_en); + break; + case ISCSI_PARAM_IP_FRAGMENT_DISABLE: + len = sprintf(buf, "%u\n", conn->fragment_disable); + break; + case ISCSI_PARAM_IPV4_TOS: + len = sprintf(buf, "%u\n", conn->ipv4_tos); + break; + case ISCSI_PARAM_IPV6_TC: + len = sprintf(buf, "%u\n", conn->ipv6_traffic_class); + break; + case ISCSI_PARAM_IS_FW_ASSIGNED_IPV6: + len = sprintf(buf, "%u\n", conn->is_fw_assigned_ipv6); + break; + case ISCSI_PARAM_TCP_XMIT_WSF: + len = sprintf(buf, "%u\n", conn->tcp_xmit_wsf); + break; + case ISCSI_PARAM_TCP_RECV_WSF: + len = sprintf(buf, "%u\n", conn->tcp_recv_wsf); + break; default: return -ENOSYS; } diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index 4265a4bb83cd..6ac9e17acdc4 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -62,6 +62,8 @@ enum { TMF_NOT_FOUND, }; +#define ISID_SIZE 6 + /* Connection suspend "bit" */ #define ISCSI_SUSPEND_BIT 1 @@ -173,6 +175,7 @@ struct iscsi_conn { /* iSCSI connection-wide sequencing */ uint32_t exp_statsn; + uint32_t statsn; /* control data */ int id; /* CID */ @@ -213,6 +216,22 @@ struct iscsi_conn { int persistent_port; char *persistent_address; + unsigned max_segment_size; + unsigned tcp_xmit_wsf; + unsigned tcp_recv_wsf; + uint16_t keepalive_tmo; + uint16_t local_port; + uint8_t tcp_timestamp_stat; + uint8_t tcp_nagle_disable; + uint8_t tcp_wsf_disable; + uint8_t tcp_timer_scale; + uint8_t tcp_timestamp_en; + uint8_t fragment_disable; + uint8_t ipv4_tos; + uint8_t ipv6_traffic_class; + uint8_t ipv6_flow_label; + uint8_t is_fw_assigned_ipv6; + /* MIB-statistics */ uint64_t txdata_octets; uint64_t rxdata_octets; @@ -290,6 +309,18 @@ struct iscsi_session { char *boot_root; char *boot_nic; char *boot_target; + char *portal_type; + char *discovery_parent_type; + uint16_t discovery_parent_idx; + uint16_t def_taskmgmt_tmo; + uint16_t tsid; + uint8_t auto_snd_tgt_disable; + uint8_t discovery_sess; + uint8_t chap_auth_en; + uint8_t discovery_logout_en; + uint8_t bidi_chap_en; + uint8_t discovery_auth_optional; + uint8_t isid[ISID_SIZE]; /* control data */ struct iscsi_transport *tt; -- cgit v1.2.3-71-gd317 From abec1a806e0c3cf168999667d5fb6218398ef12a Mon Sep 17 00:00:00 2001 From: Christoph Lameter Date: Fri, 23 Aug 2013 19:01:58 +0000 Subject: percpu: Make __verify_pcu_ptr handle per cpu pointers to arrays __verify_pcpu_ptr() will cause a compilation failure if the type of the pointer is a pointer to a fixed array of objects. Adding zero to the pointer converts the type of pointer to that pointing to a single object of the array. Signed-off-by: Christoph Lameter Signed-off-by: Tejun Heo --- include/linux/percpu-defs.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h index 27ef6b190ea6..57e890abe1f0 100644 --- a/include/linux/percpu-defs.h +++ b/include/linux/percpu-defs.h @@ -22,9 +22,12 @@ * Macro which verifies @ptr is a percpu pointer without evaluating * @ptr. This is to be used in percpu accessors to verify that the * input parameter is a percpu pointer. + * + * + 0 is required in order to convert the pointer type from a + * potential array type to a pointer to a single item of the array. */ #define __verify_pcpu_ptr(ptr) do { \ - const void __percpu *__vpp_verify = (typeof(ptr))NULL; \ + const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \ (void)__vpp_verify; \ } while (0) -- cgit v1.2.3-71-gd317 From a65fcce75a75c0d41b938f86d09d42b6f1733309 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 23 Aug 2013 15:02:01 -0700 Subject: sysfs: create __ATTR_WO() This creates the macro __ATTR_WO() for write-only attributes, instead of having to "open define" them. Signed-off-by: Greg Kroah-Hartman --- include/linux/sysfs.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index b5a9d9b26bd4..69c1ff003628 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -80,6 +80,11 @@ struct attribute_group { .show = _name##_show, \ } +#define __ATTR_WO(_name) { \ + .attr = { .name = __stringify(_name), .mode = S_IWUSR }, \ + .store = _name##_store, \ +} + #define __ATTR_RW(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), \ _name##_show, _name##_store) -- cgit v1.2.3-71-gd317 From 1130c55c757660ac83bd2de220f08580c56b6855 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 23 Aug 2013 15:02:56 -0700 Subject: driver core: create write-only attribute macros for devices and drivers This creates the macros DRIVER_ATTR_WO() and DEVICE_ATTR_WO() for write-only attributes for drivers and devices. Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/device.h b/include/linux/device.h index 26e23b22ed0a..0ed94b5f67fc 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -277,6 +277,8 @@ struct driver_attribute { struct driver_attribute driver_attr_##_name = __ATTR_RW(_name) #define DRIVER_ATTR_RO(_name) \ struct driver_attribute driver_attr_##_name = __ATTR_RO(_name) +#define DRIVER_ATTR_WO(_name) \ + struct driver_attribute driver_attr_##_name = __ATTR_WO(_name) extern int __must_check driver_create_file(struct device_driver *driver, const struct driver_attribute *attr); @@ -534,6 +536,8 @@ ssize_t device_store_bool(struct device *dev, struct device_attribute *attr, struct device_attribute dev_attr_##_name = __ATTR_RW(_name) #define DEVICE_ATTR_RO(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RO(_name) +#define DEVICE_ATTR_WO(_name) \ + struct device_attribute dev_attr_##_name = __ATTR_WO(_name) #define DEVICE_ULONG_ATTR(_name, _mode, _var) \ struct dev_ext_attribute dev_attr_##_name = \ { __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) } -- cgit v1.2.3-71-gd317 From 03f0d916aa0317592dda11bd17c7357858719b6c Mon Sep 17 00:00:00 2001 From: Andy Zhou Date: Wed, 7 Aug 2013 20:01:00 -0700 Subject: openvswitch: Mega flow implementation Add wildcarded flow support in kernel datapath. Wildcarded flow can improve OVS flow set up performance by avoid sending matching new flows to the user space program. The exact performance boost will largely dependent on wildcarded flow hit rate. In case all new flows hits wildcard flows, the flow set up rate is within 5% of that of linux bridge module. Pravin has made significant contributions to this patch. Including API clean ups and bug fixes. Signed-off-by: Pravin B Shelar Signed-off-by: Andy Zhou Signed-off-by: Jesse Gross --- Documentation/networking/openvswitch.txt | 40 + include/uapi/linux/openvswitch.h | 9 +- net/openvswitch/actions.c | 6 +- net/openvswitch/datapath.c | 140 ++- net/openvswitch/datapath.h | 6 + net/openvswitch/flow.c | 1387 ++++++++++++++++++++---------- net/openvswitch/flow.h | 96 ++- 7 files changed, 1171 insertions(+), 513 deletions(-) (limited to 'include') diff --git a/Documentation/networking/openvswitch.txt b/Documentation/networking/openvswitch.txt index 8fa2dd1e792e..37c20ee2455e 100644 --- a/Documentation/networking/openvswitch.txt +++ b/Documentation/networking/openvswitch.txt @@ -91,6 +91,46 @@ Often we ellipsize arguments not important to the discussion, e.g.: in_port(1), eth(...), eth_type(0x0800), ipv4(...), tcp(...) +Wildcarded flow key format +-------------------------- + +A wildcarded flow is described with two sequences of Netlink attributes +passed over the Netlink socket. A flow key, exactly as described above, and an +optional corresponding flow mask. + +A wildcarded flow can represent a group of exact match flows. Each '1' bit +in the mask specifies a exact match with the corresponding bit in the flow key. +A '0' bit specifies a don't care bit, which will match either a '1' or '0' bit +of a incoming packet. Using wildcarded flow can improve the flow set up rate +by reduce the number of new flows need to be processed by the user space program. + +Support for the mask Netlink attribute is optional for both the kernel and user +space program. The kernel can ignore the mask attribute, installing an exact +match flow, or reduce the number of don't care bits in the kernel to less than +what was specified by the user space program. In this case, variations in bits +that the kernel does not implement will simply result in additional flow setups. +The kernel module will also work with user space programs that neither support +nor supply flow mask attributes. + +Since the kernel may ignore or modify wildcard bits, it can be difficult for +the userspace program to know exactly what matches are installed. There are +two possible approaches: reactively install flows as they miss the kernel +flow table (and therefore not attempt to determine wildcard changes at all) +or use the kernel's response messages to determine the installed wildcards. + +When interacting with userspace, the kernel should maintain the match portion +of the key exactly as originally installed. This will provides a handle to +identify the flow for all future operations. However, when reporting the +mask of an installed flow, the mask should include any restrictions imposed +by the kernel. + +The behavior when using overlapping wildcarded flows is undefined. It is the +responsibility of the user space program to ensure that any incoming packet +can match at most one flow, wildcarded or not. The current implementation +performs best-effort detection of overlapping wildcarded flows and may reject +some but not all of them. However, this behavior may change in future versions. + + Basic rule for evolving flow keys --------------------------------- diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 52490b0e62b5..de1fa5d3780f 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -1,6 +1,6 @@ /* - * Copyright (c) 2007-2011 Nicira Networks. + * Copyright (c) 2007-2013 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -379,6 +379,12 @@ struct ovs_key_nd { * @OVS_FLOW_ATTR_CLEAR: If present in a %OVS_FLOW_CMD_SET request, clears the * last-used time, accumulated TCP flags, and statistics for this flow. * Otherwise ignored in requests. Never present in notifications. + * @OVS_FLOW_ATTR_MASK: Nested %OVS_KEY_ATTR_* attributes specifying the + * mask bits for wildcarded flow match. Mask bit value '1' specifies exact + * match with corresponding flow key bit, while mask bit value '0' specifies + * a wildcarded match. Omitting attribute is treated as wildcarding all + * corresponding fields. Optional for all requests. If not present, + * all flow key bits are exact match bits. * * These attributes follow the &struct ovs_header within the Generic Netlink * payload for %OVS_FLOW_* commands. @@ -391,6 +397,7 @@ enum ovs_flow_attr { OVS_FLOW_ATTR_TCP_FLAGS, /* 8-bit OR'd TCP flags. */ OVS_FLOW_ATTR_USED, /* u64 msecs last used in monotonic time. */ OVS_FLOW_ATTR_CLEAR, /* Flag to clear stats, tcp_flags, used. */ + OVS_FLOW_ATTR_MASK, /* Sequence of OVS_KEY_ATTR_* attributes. */ __OVS_FLOW_ATTR_MAX }; diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index ab101f715447..1f680222f4f7 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2012 Nicira, Inc. + * Copyright (c) 2007-2013 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -376,8 +376,10 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb, const struct nlattr *a; int rem; + BUG_ON(!OVS_CB(skb)->pkt_key); + upcall.cmd = OVS_PACKET_CMD_ACTION; - upcall.key = &OVS_CB(skb)->flow->key; + upcall.key = OVS_CB(skb)->pkt_key; upcall.userdata = NULL; upcall.portid = 0; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 9d97ef3c9830..d29cd9aa4a67 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2012 Nicira, Inc. + * Copyright (c) 2007-2013 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -165,7 +165,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu) { struct datapath *dp = container_of(rcu, struct datapath, rcu); - ovs_flow_tbl_destroy((__force struct flow_table *)dp->table); + ovs_flow_tbl_destroy((__force struct flow_table *)dp->table, false); free_percpu(dp->stats_percpu); release_net(ovs_dp_get_net(dp)); kfree(dp->ports); @@ -226,19 +226,18 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) struct sw_flow_key key; u64 *stats_counter; int error; - int key_len; stats = this_cpu_ptr(dp->stats_percpu); /* Extract flow from 'skb' into 'key'. */ - error = ovs_flow_extract(skb, p->port_no, &key, &key_len); + error = ovs_flow_extract(skb, p->port_no, &key); if (unlikely(error)) { kfree_skb(skb); return; } /* Look up flow. */ - flow = ovs_flow_tbl_lookup(rcu_dereference(dp->table), &key, key_len); + flow = ovs_flow_lookup(rcu_dereference(dp->table), &key); if (unlikely(!flow)) { struct dp_upcall_info upcall; @@ -253,6 +252,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) } OVS_CB(skb)->flow = flow; + OVS_CB(skb)->pkt_key = &key; stats_counter = &stats->n_hit; ovs_flow_used(OVS_CB(skb)->flow, skb); @@ -435,7 +435,7 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex, upcall->dp_ifindex = dp_ifindex; nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_KEY); - ovs_flow_to_nlattrs(upcall_info->key, user_skb); + ovs_flow_to_nlattrs(upcall_info->key, upcall_info->key, user_skb); nla_nest_end(user_skb, nla); if (upcall_info->userdata) @@ -468,7 +468,7 @@ static int flush_flows(struct datapath *dp) rcu_assign_pointer(dp->table, new_table); - ovs_flow_tbl_deferred_destroy(old_table); + ovs_flow_tbl_destroy(old_table, true); return 0; } @@ -611,10 +611,12 @@ static int validate_tp_port(const struct sw_flow_key *flow_key) static int validate_and_copy_set_tun(const struct nlattr *attr, struct sw_flow_actions **sfa) { - struct ovs_key_ipv4_tunnel tun_key; + struct sw_flow_match match; + struct sw_flow_key key; int err, start; - err = ovs_ipv4_tun_from_nlattr(nla_data(attr), &tun_key); + ovs_match_init(&match, &key, NULL); + err = ovs_ipv4_tun_from_nlattr(nla_data(attr), &match, false); if (err) return err; @@ -622,7 +624,8 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, if (start < 0) return start; - err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &tun_key, sizeof(tun_key)); + err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &match.key->tun_key, + sizeof(match.key->tun_key)); add_nested_action_end(*sfa, start); return err; @@ -857,7 +860,6 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) struct ethhdr *eth; int len; int err; - int key_len; err = -EINVAL; if (!a[OVS_PACKET_ATTR_PACKET] || !a[OVS_PACKET_ATTR_KEY] || @@ -890,11 +892,11 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(flow)) goto err_kfree_skb; - err = ovs_flow_extract(packet, -1, &flow->key, &key_len); + err = ovs_flow_extract(packet, -1, &flow->key); if (err) goto err_flow_free; - err = ovs_flow_metadata_from_nlattrs(flow, key_len, a[OVS_PACKET_ATTR_KEY]); + err = ovs_flow_metadata_from_nlattrs(flow, a[OVS_PACKET_ATTR_KEY]); if (err) goto err_flow_free; acts = ovs_flow_actions_alloc(nla_len(a[OVS_PACKET_ATTR_ACTIONS])); @@ -908,6 +910,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) goto err_flow_free; OVS_CB(packet)->flow = flow; + OVS_CB(packet)->pkt_key = &flow->key; packet->priority = flow->key.phy.priority; packet->mark = flow->key.phy.skb_mark; @@ -922,13 +925,13 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) local_bh_enable(); rcu_read_unlock(); - ovs_flow_free(flow); + ovs_flow_free(flow, false); return err; err_unlock: rcu_read_unlock(); err_flow_free: - ovs_flow_free(flow); + ovs_flow_free(flow, false); err_kfree_skb: kfree_skb(packet); err: @@ -1045,7 +1048,8 @@ static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb) if (!start) return -EMSGSIZE; - err = ovs_ipv4_tun_to_nlattr(skb, nla_data(ovs_key)); + err = ovs_ipv4_tun_to_nlattr(skb, nla_data(ovs_key), + nla_data(ovs_key)); if (err) return err; nla_nest_end(skb, start); @@ -1093,6 +1097,7 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts) { return NLMSG_ALIGN(sizeof(struct ovs_header)) + nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_KEY */ + + nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_MASK */ + nla_total_size(sizeof(struct ovs_flow_stats)) /* OVS_FLOW_ATTR_STATS */ + nla_total_size(1) /* OVS_FLOW_ATTR_TCP_FLAGS */ + nla_total_size(8) /* OVS_FLOW_ATTR_USED */ @@ -1119,12 +1124,25 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, ovs_header->dp_ifindex = get_dpifindex(dp); + /* Fill flow key. */ nla = nla_nest_start(skb, OVS_FLOW_ATTR_KEY); if (!nla) goto nla_put_failure; - err = ovs_flow_to_nlattrs(&flow->key, skb); + + err = ovs_flow_to_nlattrs(&flow->unmasked_key, + &flow->unmasked_key, skb); + if (err) + goto error; + nla_nest_end(skb, nla); + + nla = nla_nest_start(skb, OVS_FLOW_ATTR_MASK); + if (!nla) + goto nla_put_failure; + + err = ovs_flow_to_nlattrs(&flow->key, &flow->mask->key, skb); if (err) goto error; + nla_nest_end(skb, nla); spin_lock_bh(&flow->lock); @@ -1214,20 +1232,24 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) { struct nlattr **a = info->attrs; struct ovs_header *ovs_header = info->userhdr; - struct sw_flow_key key; - struct sw_flow *flow; + struct sw_flow_key key, masked_key; + struct sw_flow *flow = NULL; + struct sw_flow_mask mask; struct sk_buff *reply; struct datapath *dp; struct flow_table *table; struct sw_flow_actions *acts = NULL; + struct sw_flow_match match; int error; - int key_len; /* Extract key. */ error = -EINVAL; if (!a[OVS_FLOW_ATTR_KEY]) goto error; - error = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]); + + ovs_match_init(&match, &key, &mask); + error = ovs_match_from_nlattrs(&match, + a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]); if (error) goto error; @@ -1238,9 +1260,13 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(acts)) goto error; - error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &key, 0, &acts); - if (error) + ovs_flow_key_mask(&masked_key, &key, &mask); + error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], + &masked_key, 0, &acts); + if (error) { + OVS_NLERR("Flow actions may not be safe on all matching packets.\n"); goto err_kfree; + } } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) { error = -EINVAL; goto error; @@ -1253,8 +1279,11 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) goto err_unlock_ovs; table = ovsl_dereference(dp->table); - flow = ovs_flow_tbl_lookup(table, &key, key_len); + + /* Check if this is a duplicate flow */ + flow = ovs_flow_lookup(table, &key); if (!flow) { + struct sw_flow_mask *mask_p; /* Bail out if we're not allowed to create a new flow. */ error = -ENOENT; if (info->genlhdr->cmd == OVS_FLOW_CMD_SET) @@ -1267,7 +1296,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) new_table = ovs_flow_tbl_expand(table); if (!IS_ERR(new_table)) { rcu_assign_pointer(dp->table, new_table); - ovs_flow_tbl_deferred_destroy(table); + ovs_flow_tbl_destroy(table, true); table = ovsl_dereference(dp->table); } } @@ -1280,14 +1309,30 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) } clear_stats(flow); + flow->key = masked_key; + flow->unmasked_key = key; + + /* Make sure mask is unique in the system */ + mask_p = ovs_sw_flow_mask_find(table, &mask); + if (!mask_p) { + /* Allocate a new mask if none exsits. */ + mask_p = ovs_sw_flow_mask_alloc(); + if (!mask_p) + goto err_flow_free; + mask_p->key = mask.key; + mask_p->range = mask.range; + ovs_sw_flow_mask_insert(table, mask_p); + } + + ovs_sw_flow_mask_add_ref(mask_p); + flow->mask = mask_p; rcu_assign_pointer(flow->sf_acts, acts); /* Put flow in bucket. */ - ovs_flow_tbl_insert(table, flow, &key, key_len); + ovs_flow_insert(table, flow); reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid, - info->snd_seq, - OVS_FLOW_CMD_NEW); + info->snd_seq, OVS_FLOW_CMD_NEW); } else { /* We found a matching flow. */ struct sw_flow_actions *old_acts; @@ -1303,6 +1348,13 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) info->nlhdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_EXCL)) goto err_unlock_ovs; + /* The unmasked key has to be the same for flow updates. */ + error = -EINVAL; + if (!ovs_flow_cmp_unmasked_key(flow, &key, match.range.end)) { + OVS_NLERR("Flow modification message rejected, unmasked key does not match.\n"); + goto err_unlock_ovs; + } + /* Update actions. */ old_acts = ovsl_dereference(flow->sf_acts); rcu_assign_pointer(flow->sf_acts, acts); @@ -1327,6 +1379,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) ovs_dp_flow_multicast_group.id, PTR_ERR(reply)); return 0; +err_flow_free: + ovs_flow_free(flow, false); err_unlock_ovs: ovs_unlock(); err_kfree: @@ -1344,12 +1398,16 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) struct sw_flow *flow; struct datapath *dp; struct flow_table *table; + struct sw_flow_match match; int err; - int key_len; - if (!a[OVS_FLOW_ATTR_KEY]) + if (!a[OVS_FLOW_ATTR_KEY]) { + OVS_NLERR("Flow get message rejected, Key attribute missing.\n"); return -EINVAL; - err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]); + } + + ovs_match_init(&match, &key, NULL); + err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY], NULL); if (err) return err; @@ -1361,7 +1419,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) } table = ovsl_dereference(dp->table); - flow = ovs_flow_tbl_lookup(table, &key, key_len); + flow = ovs_flow_lookup_unmasked_key(table, &match); if (!flow) { err = -ENOENT; goto unlock; @@ -1390,8 +1448,8 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) struct sw_flow *flow; struct datapath *dp; struct flow_table *table; + struct sw_flow_match match; int err; - int key_len; ovs_lock(); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); @@ -1404,12 +1462,14 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) err = flush_flows(dp); goto unlock; } - err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]); + + ovs_match_init(&match, &key, NULL); + err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY], NULL); if (err) goto unlock; table = ovsl_dereference(dp->table); - flow = ovs_flow_tbl_lookup(table, &key, key_len); + flow = ovs_flow_lookup_unmasked_key(table, &match); if (!flow) { err = -ENOENT; goto unlock; @@ -1421,13 +1481,13 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) goto unlock; } - ovs_flow_tbl_remove(table, flow); + ovs_flow_remove(table, flow); err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid, info->snd_seq, 0, OVS_FLOW_CMD_DEL); BUG_ON(err < 0); - ovs_flow_deferred_free(flow); + ovs_flow_free(flow, true); ovs_unlock(); ovs_notify(reply, info, &ovs_dp_flow_multicast_group); @@ -1457,7 +1517,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) bucket = cb->args[0]; obj = cb->args[1]; - flow = ovs_flow_tbl_next(table, &bucket, &obj); + flow = ovs_flow_dump_next(table, &bucket, &obj); if (!flow) break; @@ -1680,7 +1740,7 @@ err_destroy_ports_array: err_destroy_percpu: free_percpu(dp->stats_percpu); err_destroy_table: - ovs_flow_tbl_destroy(ovsl_dereference(dp->table)); + ovs_flow_tbl_destroy(ovsl_dereference(dp->table), false); err_free_dp: release_net(ovs_dp_get_net(dp)); kfree(dp); @@ -2287,7 +2347,7 @@ static void rehash_flow_table(struct work_struct *work) new_table = ovs_flow_tbl_rehash(old_table); if (!IS_ERR(new_table)) { rcu_assign_pointer(dp->table, new_table); - ovs_flow_tbl_deferred_destroy(old_table); + ovs_flow_tbl_destroy(old_table, true); } } } diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index a91486484916..4d109c176ef3 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -88,11 +88,13 @@ struct datapath { /** * struct ovs_skb_cb - OVS data in skb CB * @flow: The flow associated with this packet. May be %NULL if no flow. + * @pkt_key: The flow information extracted from the packet. Must be nonnull. * @tun_key: Key for the tunnel that encapsulated this packet. NULL if the * packet is not being tunneled. */ struct ovs_skb_cb { struct sw_flow *flow; + struct sw_flow_key *pkt_key; struct ovs_key_ipv4_tunnel *tun_key; }; #define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb) @@ -183,4 +185,8 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq, int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb); void ovs_dp_notify_wq(struct work_struct *work); + +#define OVS_NLERR(fmt, ...) \ + pr_info_once("netlink: " fmt, ##__VA_ARGS__) + #endif /* datapath.h */ diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index fca282520cee..1fceb9653598 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2011 Nicira, Inc. + * Copyright (c) 2007-2013 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -46,6 +46,184 @@ static struct kmem_cache *flow_cache; +static void ovs_sw_flow_mask_set(struct sw_flow_mask *mask, + struct sw_flow_key_range *range, u8 val); + +static void update_range__(struct sw_flow_match *match, + size_t offset, size_t size, bool is_mask) +{ + struct sw_flow_key_range *range = NULL; + size_t start = offset; + size_t end = offset + size; + + if (!is_mask) + range = &match->range; + else if (match->mask) + range = &match->mask->range; + + if (!range) + return; + + if (range->start == range->end) { + range->start = start; + range->end = end; + return; + } + + if (range->start > start) + range->start = start; + + if (range->end < end) + range->end = end; +} + +#define SW_FLOW_KEY_PUT(match, field, value, is_mask) \ + do { \ + update_range__(match, offsetof(struct sw_flow_key, field), \ + sizeof((match)->key->field), is_mask); \ + if (is_mask) { \ + if ((match)->mask) \ + (match)->mask->key.field = value; \ + } else { \ + (match)->key->field = value; \ + } \ + } while (0) + +#define SW_FLOW_KEY_MEMCPY(match, field, value_p, len, is_mask) \ + do { \ + update_range__(match, offsetof(struct sw_flow_key, field), \ + len, is_mask); \ + if (is_mask) { \ + if ((match)->mask) \ + memcpy(&(match)->mask->key.field, value_p, len);\ + } else { \ + memcpy(&(match)->key->field, value_p, len); \ + } \ + } while (0) + +void ovs_match_init(struct sw_flow_match *match, + struct sw_flow_key *key, + struct sw_flow_mask *mask) +{ + memset(match, 0, sizeof(*match)); + match->key = key; + match->mask = mask; + + memset(key, 0, sizeof(*key)); + + if (mask) { + memset(&mask->key, 0, sizeof(mask->key)); + mask->range.start = mask->range.end = 0; + } +} + +static bool ovs_match_validate(const struct sw_flow_match *match, + u64 key_attrs, u64 mask_attrs) +{ + u64 key_expected = 1 << OVS_KEY_ATTR_ETHERNET; + u64 mask_allowed = key_attrs; /* At most allow all key attributes */ + + /* The following mask attributes allowed only if they + * pass the validation tests. */ + mask_allowed &= ~((1 << OVS_KEY_ATTR_IPV4) + | (1 << OVS_KEY_ATTR_IPV6) + | (1 << OVS_KEY_ATTR_TCP) + | (1 << OVS_KEY_ATTR_UDP) + | (1 << OVS_KEY_ATTR_ICMP) + | (1 << OVS_KEY_ATTR_ICMPV6) + | (1 << OVS_KEY_ATTR_ARP) + | (1 << OVS_KEY_ATTR_ND)); + + /* Always allowed mask fields. */ + mask_allowed |= ((1 << OVS_KEY_ATTR_TUNNEL) + | (1 << OVS_KEY_ATTR_IN_PORT) + | (1 << OVS_KEY_ATTR_ETHERTYPE)); + + /* Check key attributes. */ + if (match->key->eth.type == htons(ETH_P_ARP) + || match->key->eth.type == htons(ETH_P_RARP)) { + key_expected |= 1 << OVS_KEY_ATTR_ARP; + if (match->mask && (match->mask->key.eth.type == htons(0xffff))) + mask_allowed |= 1 << OVS_KEY_ATTR_ARP; + } + + if (match->key->eth.type == htons(ETH_P_IP)) { + key_expected |= 1 << OVS_KEY_ATTR_IPV4; + if (match->mask && (match->mask->key.eth.type == htons(0xffff))) + mask_allowed |= 1 << OVS_KEY_ATTR_IPV4; + + if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) { + if (match->key->ip.proto == IPPROTO_UDP) { + key_expected |= 1 << OVS_KEY_ATTR_UDP; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_UDP; + } + + if (match->key->ip.proto == IPPROTO_TCP) { + key_expected |= 1 << OVS_KEY_ATTR_TCP; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_TCP; + } + + if (match->key->ip.proto == IPPROTO_ICMP) { + key_expected |= 1 << OVS_KEY_ATTR_ICMP; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_ICMP; + } + } + } + + if (match->key->eth.type == htons(ETH_P_IPV6)) { + key_expected |= 1 << OVS_KEY_ATTR_IPV6; + if (match->mask && (match->mask->key.eth.type == htons(0xffff))) + mask_allowed |= 1 << OVS_KEY_ATTR_IPV6; + + if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) { + if (match->key->ip.proto == IPPROTO_UDP) { + key_expected |= 1 << OVS_KEY_ATTR_UDP; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_UDP; + } + + if (match->key->ip.proto == IPPROTO_TCP) { + key_expected |= 1 << OVS_KEY_ATTR_TCP; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_TCP; + } + + if (match->key->ip.proto == IPPROTO_ICMPV6) { + key_expected |= 1 << OVS_KEY_ATTR_ICMPV6; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_ICMPV6; + + if (match->key->ipv6.tp.src == + htons(NDISC_NEIGHBOUR_SOLICITATION) || + match->key->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) { + key_expected |= 1 << OVS_KEY_ATTR_ND; + if (match->mask && (match->mask->key.ipv6.tp.src == htons(0xffff))) + mask_allowed |= 1 << OVS_KEY_ATTR_ND; + } + } + } + } + + if ((key_attrs & key_expected) != key_expected) { + /* Key attributes check failed. */ + OVS_NLERR("Missing expected key attributes (key_attrs=%llx, expected=%llx).\n", + key_attrs, key_expected); + return false; + } + + if ((mask_attrs & mask_allowed) != mask_attrs) { + /* Mask attributes check failed. */ + OVS_NLERR("Contain more than allowed mask fields (mask_attrs=%llx, mask_allowed=%llx).\n", + mask_attrs, mask_allowed); + return false; + } + + return true; +} + static int check_header(struct sk_buff *skb, int len) { if (unlikely(skb->len < len)) @@ -121,12 +299,7 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies) return cur_ms - idle_ms; } -#define SW_FLOW_KEY_OFFSET(field) \ - (offsetof(struct sw_flow_key, field) + \ - FIELD_SIZEOF(struct sw_flow_key, field)) - -static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key, - int *key_lenp) +static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key) { unsigned int nh_ofs = skb_network_offset(skb); unsigned int nh_len; @@ -136,8 +309,6 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key, __be16 frag_off; int err; - *key_lenp = SW_FLOW_KEY_OFFSET(ipv6.label); - err = check_header(skb, nh_ofs + sizeof(*nh)); if (unlikely(err)) return err; @@ -176,6 +347,21 @@ static bool icmp6hdr_ok(struct sk_buff *skb) sizeof(struct icmp6hdr)); } +void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src, + const struct sw_flow_mask *mask) +{ + u8 *m = (u8 *)&mask->key + mask->range.start; + u8 *s = (u8 *)src + mask->range.start; + u8 *d = (u8 *)dst + mask->range.start; + int i; + + memset(dst, 0, sizeof(*dst)); + for (i = 0; i < ovs_sw_flow_mask_size_roundup(mask); i++) { + *d = *s & *m; + d++, s++, m++; + } +} + #define TCP_FLAGS_OFFSET 13 #define TCP_FLAG_MASK 0x3f @@ -224,6 +410,7 @@ struct sw_flow *ovs_flow_alloc(void) spin_lock_init(&flow->lock); flow->sf_acts = NULL; + flow->mask = NULL; return flow; } @@ -263,7 +450,7 @@ static void free_buckets(struct flex_array *buckets) flex_array_free(buckets); } -struct flow_table *ovs_flow_tbl_alloc(int new_size) +static struct flow_table *__flow_tbl_alloc(int new_size) { struct flow_table *table = kmalloc(sizeof(*table), GFP_KERNEL); @@ -281,17 +468,15 @@ struct flow_table *ovs_flow_tbl_alloc(int new_size) table->node_ver = 0; table->keep_flows = false; get_random_bytes(&table->hash_seed, sizeof(u32)); + table->mask_list = NULL; return table; } -void ovs_flow_tbl_destroy(struct flow_table *table) +static void __flow_tbl_destroy(struct flow_table *table) { int i; - if (!table) - return; - if (table->keep_flows) goto skip_flows; @@ -303,31 +488,55 @@ void ovs_flow_tbl_destroy(struct flow_table *table) hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) { hlist_del(&flow->hash_node[ver]); - ovs_flow_free(flow); + ovs_flow_free(flow, false); } } + BUG_ON(!list_empty(table->mask_list)); + kfree(table->mask_list); + skip_flows: free_buckets(table->buckets); kfree(table); } +struct flow_table *ovs_flow_tbl_alloc(int new_size) +{ + struct flow_table *table = __flow_tbl_alloc(new_size); + + if (!table) + return NULL; + + table->mask_list = kmalloc(sizeof(struct list_head), GFP_KERNEL); + if (!table->mask_list) { + table->keep_flows = true; + __flow_tbl_destroy(table); + return NULL; + } + INIT_LIST_HEAD(table->mask_list); + + return table; +} + static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu) { struct flow_table *table = container_of(rcu, struct flow_table, rcu); - ovs_flow_tbl_destroy(table); + __flow_tbl_destroy(table); } -void ovs_flow_tbl_deferred_destroy(struct flow_table *table) +void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred) { if (!table) return; - call_rcu(&table->rcu, flow_tbl_destroy_rcu_cb); + if (deferred) + call_rcu(&table->rcu, flow_tbl_destroy_rcu_cb); + else + __flow_tbl_destroy(table); } -struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *last) +struct sw_flow *ovs_flow_dump_next(struct flow_table *table, u32 *bucket, u32 *last) { struct sw_flow *flow; struct hlist_head *head; @@ -353,11 +562,13 @@ struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *la return NULL; } -static void __flow_tbl_insert(struct flow_table *table, struct sw_flow *flow) +static void __tbl_insert(struct flow_table *table, struct sw_flow *flow) { struct hlist_head *head; + head = find_bucket(table, flow->hash); hlist_add_head_rcu(&flow->hash_node[table->node_ver], head); + table->count++; } @@ -377,8 +588,10 @@ static void flow_table_copy_flows(struct flow_table *old, struct flow_table *new head = flex_array_get(old->buckets, i); hlist_for_each_entry(flow, head, hash_node[old_ver]) - __flow_tbl_insert(new, flow); + __tbl_insert(new, flow); } + + new->mask_list = old->mask_list; old->keep_flows = true; } @@ -386,7 +599,7 @@ static struct flow_table *__flow_tbl_rehash(struct flow_table *table, int n_buck { struct flow_table *new_table; - new_table = ovs_flow_tbl_alloc(n_buckets); + new_table = __flow_tbl_alloc(n_buckets); if (!new_table) return ERR_PTR(-ENOMEM); @@ -405,28 +618,30 @@ struct flow_table *ovs_flow_tbl_expand(struct flow_table *table) return __flow_tbl_rehash(table, table->n_buckets * 2); } -void ovs_flow_free(struct sw_flow *flow) +static void __flow_free(struct sw_flow *flow) { - if (unlikely(!flow)) - return; - kfree((struct sf_flow_acts __force *)flow->sf_acts); kmem_cache_free(flow_cache, flow); } -/* RCU callback used by ovs_flow_deferred_free. */ static void rcu_free_flow_callback(struct rcu_head *rcu) { struct sw_flow *flow = container_of(rcu, struct sw_flow, rcu); - ovs_flow_free(flow); + __flow_free(flow); } -/* Schedules 'flow' to be freed after the next RCU grace period. - * The caller must hold rcu_read_lock for this to be sensible. */ -void ovs_flow_deferred_free(struct sw_flow *flow) +void ovs_flow_free(struct sw_flow *flow, bool deferred) { - call_rcu(&flow->rcu, rcu_free_flow_callback); + if (!flow) + return; + + ovs_sw_flow_mask_del_ref(flow->mask, deferred); + + if (deferred) + call_rcu(&flow->rcu, rcu_free_flow_callback); + else + __flow_free(flow); } /* Schedules 'sf_acts' to be freed after the next RCU grace period. @@ -497,18 +712,15 @@ static __be16 parse_ethertype(struct sk_buff *skb) } static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key, - int *key_lenp, int nh_len) + int nh_len) { struct icmp6hdr *icmp = icmp6_hdr(skb); - int error = 0; - int key_len; /* The ICMPv6 type and code fields use the 16-bit transport port * fields, so we need to store them in 16-bit network byte order. */ key->ipv6.tp.src = htons(icmp->icmp6_type); key->ipv6.tp.dst = htons(icmp->icmp6_code); - key_len = SW_FLOW_KEY_OFFSET(ipv6.tp); if (icmp->icmp6_code == 0 && (icmp->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION || @@ -517,21 +729,17 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key, struct nd_msg *nd; int offset; - key_len = SW_FLOW_KEY_OFFSET(ipv6.nd); - /* In order to process neighbor discovery options, we need the * entire packet. */ if (unlikely(icmp_len < sizeof(*nd))) - goto out; - if (unlikely(skb_linearize(skb))) { - error = -ENOMEM; - goto out; - } + return 0; + + if (unlikely(skb_linearize(skb))) + return -ENOMEM; nd = (struct nd_msg *)skb_transport_header(skb); key->ipv6.nd.target = nd->target; - key_len = SW_FLOW_KEY_OFFSET(ipv6.nd); icmp_len -= sizeof(*nd); offset = 0; @@ -541,7 +749,7 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key, int opt_len = nd_opt->nd_opt_len * 8; if (unlikely(!opt_len || opt_len > icmp_len)) - goto invalid; + return 0; /* Store the link layer address if the appropriate * option is provided. It is considered an error if @@ -566,16 +774,14 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key, } } - goto out; + return 0; invalid: memset(&key->ipv6.nd.target, 0, sizeof(key->ipv6.nd.target)); memset(key->ipv6.nd.sll, 0, sizeof(key->ipv6.nd.sll)); memset(key->ipv6.nd.tll, 0, sizeof(key->ipv6.nd.tll)); -out: - *key_lenp = key_len; - return error; + return 0; } /** @@ -584,7 +790,6 @@ out: * Ethernet header * @in_port: port number on which @skb was received. * @key: output flow key - * @key_lenp: length of output flow key * * The caller must ensure that skb->len >= ETH_HLEN. * @@ -602,11 +807,9 @@ out: * of a correct length, otherwise the same as skb->network_header. * For other key->eth.type values it is left untouched. */ -int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, - int *key_lenp) +int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) { - int error = 0; - int key_len = SW_FLOW_KEY_OFFSET(eth); + int error; struct ethhdr *eth; memset(key, 0, sizeof(*key)); @@ -649,15 +852,13 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, struct iphdr *nh; __be16 offset; - key_len = SW_FLOW_KEY_OFFSET(ipv4.addr); - error = check_iphdr(skb); if (unlikely(error)) { if (error == -EINVAL) { skb->transport_header = skb->network_header; error = 0; } - goto out; + return error; } nh = ip_hdr(skb); @@ -671,7 +872,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, offset = nh->frag_off & htons(IP_OFFSET); if (offset) { key->ip.frag = OVS_FRAG_TYPE_LATER; - goto out; + return 0; } if (nh->frag_off & htons(IP_MF) || skb_shinfo(skb)->gso_type & SKB_GSO_UDP) @@ -679,21 +880,18 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, /* Transport layer. */ if (key->ip.proto == IPPROTO_TCP) { - key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); if (tcphdr_ok(skb)) { struct tcphdr *tcp = tcp_hdr(skb); key->ipv4.tp.src = tcp->source; key->ipv4.tp.dst = tcp->dest; } } else if (key->ip.proto == IPPROTO_UDP) { - key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); if (udphdr_ok(skb)) { struct udphdr *udp = udp_hdr(skb); key->ipv4.tp.src = udp->source; key->ipv4.tp.dst = udp->dest; } } else if (key->ip.proto == IPPROTO_ICMP) { - key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); if (icmphdr_ok(skb)) { struct icmphdr *icmp = icmp_hdr(skb); /* The ICMP type and code fields use the 16-bit @@ -722,53 +920,49 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, memcpy(&key->ipv4.addr.dst, arp->ar_tip, sizeof(key->ipv4.addr.dst)); memcpy(key->ipv4.arp.sha, arp->ar_sha, ETH_ALEN); memcpy(key->ipv4.arp.tha, arp->ar_tha, ETH_ALEN); - key_len = SW_FLOW_KEY_OFFSET(ipv4.arp); } } else if (key->eth.type == htons(ETH_P_IPV6)) { int nh_len; /* IPv6 Header + Extensions */ - nh_len = parse_ipv6hdr(skb, key, &key_len); + nh_len = parse_ipv6hdr(skb, key); if (unlikely(nh_len < 0)) { - if (nh_len == -EINVAL) + if (nh_len == -EINVAL) { skb->transport_header = skb->network_header; - else + error = 0; + } else { error = nh_len; - goto out; + } + return error; } if (key->ip.frag == OVS_FRAG_TYPE_LATER) - goto out; + return 0; if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP) key->ip.frag = OVS_FRAG_TYPE_FIRST; /* Transport layer. */ if (key->ip.proto == NEXTHDR_TCP) { - key_len = SW_FLOW_KEY_OFFSET(ipv6.tp); if (tcphdr_ok(skb)) { struct tcphdr *tcp = tcp_hdr(skb); key->ipv6.tp.src = tcp->source; key->ipv6.tp.dst = tcp->dest; } } else if (key->ip.proto == NEXTHDR_UDP) { - key_len = SW_FLOW_KEY_OFFSET(ipv6.tp); if (udphdr_ok(skb)) { struct udphdr *udp = udp_hdr(skb); key->ipv6.tp.src = udp->source; key->ipv6.tp.dst = udp->dest; } } else if (key->ip.proto == NEXTHDR_ICMP) { - key_len = SW_FLOW_KEY_OFFSET(ipv6.tp); if (icmp6hdr_ok(skb)) { - error = parse_icmpv6(skb, key, &key_len, nh_len); - if (error < 0) - goto out; + error = parse_icmpv6(skb, key, nh_len); + if (error) + return error; } } } -out: - *key_lenp = key_len; - return error; + return 0; } static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start, int key_len) @@ -777,7 +971,7 @@ static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start, int key_l DIV_ROUND_UP(key_len - key_start, sizeof(u32)), 0); } -static int flow_key_start(struct sw_flow_key *key) +static int flow_key_start(const struct sw_flow_key *key) { if (key->tun_key.ipv4_dst) return 0; @@ -785,39 +979,95 @@ static int flow_key_start(struct sw_flow_key *key) return offsetof(struct sw_flow_key, phy); } -struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *table, - struct sw_flow_key *key, int key_len) +static bool __cmp_key(const struct sw_flow_key *key1, + const struct sw_flow_key *key2, int key_start, int key_len) +{ + return !memcmp((u8 *)key1 + key_start, + (u8 *)key2 + key_start, (key_len - key_start)); +} + +static bool __flow_cmp_key(const struct sw_flow *flow, + const struct sw_flow_key *key, int key_start, int key_len) +{ + return __cmp_key(&flow->key, key, key_start, key_len); +} + +static bool __flow_cmp_unmasked_key(const struct sw_flow *flow, + const struct sw_flow_key *key, int key_start, int key_len) +{ + return __cmp_key(&flow->unmasked_key, key, key_start, key_len); +} + +bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, + const struct sw_flow_key *key, int key_len) +{ + int key_start; + key_start = flow_key_start(key); + + return __flow_cmp_unmasked_key(flow, key, key_start, key_len); + +} + +struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table, + struct sw_flow_match *match) +{ + struct sw_flow_key *unmasked = match->key; + int key_len = match->range.end; + struct sw_flow *flow; + + flow = ovs_flow_lookup(table, unmasked); + if (flow && (!ovs_flow_cmp_unmasked_key(flow, unmasked, key_len))) + flow = NULL; + + return flow; +} + +static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table, + const struct sw_flow_key *flow_key, + struct sw_flow_mask *mask) { struct sw_flow *flow; struct hlist_head *head; - u8 *_key; - int key_start; + int key_start = mask->range.start; + int key_len = mask->range.end; u32 hash; + struct sw_flow_key masked_key; - key_start = flow_key_start(key); - hash = ovs_flow_hash(key, key_start, key_len); - - _key = (u8 *) key + key_start; + ovs_flow_key_mask(&masked_key, flow_key, mask); + hash = ovs_flow_hash(&masked_key, key_start, key_len); head = find_bucket(table, hash); hlist_for_each_entry_rcu(flow, head, hash_node[table->node_ver]) { - - if (flow->hash == hash && - !memcmp((u8 *)&flow->key + key_start, _key, key_len - key_start)) { + if (flow->mask == mask && + __flow_cmp_key(flow, &masked_key, key_start, key_len)) return flow; - } } return NULL; } -void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow, - struct sw_flow_key *key, int key_len) +struct sw_flow *ovs_flow_lookup(struct flow_table *tbl, + const struct sw_flow_key *key) { - flow->hash = ovs_flow_hash(key, flow_key_start(key), key_len); - memcpy(&flow->key, key, sizeof(flow->key)); - __flow_tbl_insert(table, flow); + struct sw_flow *flow = NULL; + struct sw_flow_mask *mask; + + list_for_each_entry_rcu(mask, tbl->mask_list, list) { + flow = ovs_masked_flow_lookup(tbl, key, mask); + if (flow) /* Found */ + break; + } + + return flow; } -void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow) + +void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow) +{ + flow->hash = ovs_flow_hash(&flow->key, flow->mask->range.start, + flow->mask->range.end); + __tbl_insert(table, flow); +} + +void ovs_flow_remove(struct flow_table *table, struct sw_flow *flow) { BUG_ON(table->count == 0); hlist_del_rcu(&flow->hash_node[table->node_ver]); @@ -844,149 +1094,84 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_TUNNEL] = -1, }; -static int ipv4_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len, - const struct nlattr *a[], u32 *attrs) -{ - const struct ovs_key_icmp *icmp_key; - const struct ovs_key_tcp *tcp_key; - const struct ovs_key_udp *udp_key; - - switch (swkey->ip.proto) { - case IPPROTO_TCP: - if (!(*attrs & (1 << OVS_KEY_ATTR_TCP))) - return -EINVAL; - *attrs &= ~(1 << OVS_KEY_ATTR_TCP); - - *key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); - tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]); - swkey->ipv4.tp.src = tcp_key->tcp_src; - swkey->ipv4.tp.dst = tcp_key->tcp_dst; - break; - - case IPPROTO_UDP: - if (!(*attrs & (1 << OVS_KEY_ATTR_UDP))) - return -EINVAL; - *attrs &= ~(1 << OVS_KEY_ATTR_UDP); - - *key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); - udp_key = nla_data(a[OVS_KEY_ATTR_UDP]); - swkey->ipv4.tp.src = udp_key->udp_src; - swkey->ipv4.tp.dst = udp_key->udp_dst; - break; - - case IPPROTO_ICMP: - if (!(*attrs & (1 << OVS_KEY_ATTR_ICMP))) - return -EINVAL; - *attrs &= ~(1 << OVS_KEY_ATTR_ICMP); - - *key_len = SW_FLOW_KEY_OFFSET(ipv4.tp); - icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]); - swkey->ipv4.tp.src = htons(icmp_key->icmp_type); - swkey->ipv4.tp.dst = htons(icmp_key->icmp_code); - break; - } - - return 0; -} - -static int ipv6_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len, - const struct nlattr *a[], u32 *attrs) +static bool is_all_zero(const u8 *fp, size_t size) { - const struct ovs_key_icmpv6 *icmpv6_key; - const struct ovs_key_tcp *tcp_key; - const struct ovs_key_udp *udp_key; - - switch (swkey->ip.proto) { - case IPPROTO_TCP: - if (!(*attrs & (1 << OVS_KEY_ATTR_TCP))) - return -EINVAL; - *attrs &= ~(1 << OVS_KEY_ATTR_TCP); - - *key_len = SW_FLOW_KEY_OFFSET(ipv6.tp); - tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]); - swkey->ipv6.tp.src = tcp_key->tcp_src; - swkey->ipv6.tp.dst = tcp_key->tcp_dst; - break; - - case IPPROTO_UDP: - if (!(*attrs & (1 << OVS_KEY_ATTR_UDP))) - return -EINVAL; - *attrs &= ~(1 << OVS_KEY_ATTR_UDP); - - *key_len = SW_FLOW_KEY_OFFSET(ipv6.tp); - udp_key = nla_data(a[OVS_KEY_ATTR_UDP]); - swkey->ipv6.tp.src = udp_key->udp_src; - swkey->ipv6.tp.dst = udp_key->udp_dst; - break; - - case IPPROTO_ICMPV6: - if (!(*attrs & (1 << OVS_KEY_ATTR_ICMPV6))) - return -EINVAL; - *attrs &= ~(1 << OVS_KEY_ATTR_ICMPV6); - - *key_len = SW_FLOW_KEY_OFFSET(ipv6.tp); - icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]); - swkey->ipv6.tp.src = htons(icmpv6_key->icmpv6_type); - swkey->ipv6.tp.dst = htons(icmpv6_key->icmpv6_code); + int i; - if (swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_SOLICITATION) || - swkey->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) { - const struct ovs_key_nd *nd_key; + if (!fp) + return false; - if (!(*attrs & (1 << OVS_KEY_ATTR_ND))) - return -EINVAL; - *attrs &= ~(1 << OVS_KEY_ATTR_ND); - - *key_len = SW_FLOW_KEY_OFFSET(ipv6.nd); - nd_key = nla_data(a[OVS_KEY_ATTR_ND]); - memcpy(&swkey->ipv6.nd.target, nd_key->nd_target, - sizeof(swkey->ipv6.nd.target)); - memcpy(swkey->ipv6.nd.sll, nd_key->nd_sll, ETH_ALEN); - memcpy(swkey->ipv6.nd.tll, nd_key->nd_tll, ETH_ALEN); - } - break; - } + for (i = 0; i < size; i++) + if (fp[i]) + return false; - return 0; + return true; } -static int parse_flow_nlattrs(const struct nlattr *attr, - const struct nlattr *a[], u32 *attrsp) +static int __parse_flow_nlattrs(const struct nlattr *attr, + const struct nlattr *a[], + u64 *attrsp, bool nz) { const struct nlattr *nla; u32 attrs; int rem; - attrs = 0; + attrs = *attrsp; nla_for_each_nested(nla, attr, rem) { u16 type = nla_type(nla); int expected_len; - if (type > OVS_KEY_ATTR_MAX || attrs & (1 << type)) + if (type > OVS_KEY_ATTR_MAX) { + OVS_NLERR("Unknown key attribute (type=%d, max=%d).\n", + type, OVS_KEY_ATTR_MAX); + } + + if (attrs & (1 << type)) { + OVS_NLERR("Duplicate key attribute (type %d).\n", type); return -EINVAL; + } expected_len = ovs_key_lens[type]; - if (nla_len(nla) != expected_len && expected_len != -1) + if (nla_len(nla) != expected_len && expected_len != -1) { + OVS_NLERR("Key attribute has unexpected length (type=%d" + ", length=%d, expected=%d).\n", type, + nla_len(nla), expected_len); return -EINVAL; + } - attrs |= 1 << type; - a[type] = nla; + if (!nz || !is_all_zero(nla_data(nla), expected_len)) { + attrs |= 1 << type; + a[type] = nla; + } } - if (rem) + if (rem) { + OVS_NLERR("Message has %d unknown bytes.\n", rem); return -EINVAL; + } *attrsp = attrs; return 0; } +static int parse_flow_mask_nlattrs(const struct nlattr *attr, + const struct nlattr *a[], u64 *attrsp) +{ + return __parse_flow_nlattrs(attr, a, attrsp, true); +} + +static int parse_flow_nlattrs(const struct nlattr *attr, + const struct nlattr *a[], u64 *attrsp) +{ + return __parse_flow_nlattrs(attr, a, attrsp, false); +} + int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr, - struct ovs_key_ipv4_tunnel *tun_key) + struct sw_flow_match *match, bool is_mask) { struct nlattr *a; int rem; bool ttl = false; - - memset(tun_key, 0, sizeof(*tun_key)); + __be16 tun_flags = 0; nla_for_each_nested(a, attr, rem) { int type = nla_type(a); @@ -1000,53 +1185,78 @@ int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr, [OVS_TUNNEL_KEY_ATTR_CSUM] = 0, }; - if (type > OVS_TUNNEL_KEY_ATTR_MAX || - ovs_tunnel_key_lens[type] != nla_len(a)) + if (type > OVS_TUNNEL_KEY_ATTR_MAX) { + OVS_NLERR("Unknown IPv4 tunnel attribute (type=%d, max=%d).\n", + type, OVS_TUNNEL_KEY_ATTR_MAX); return -EINVAL; + } + + if (ovs_tunnel_key_lens[type] != nla_len(a)) { + OVS_NLERR("IPv4 tunnel attribute type has unexpected " + " length (type=%d, length=%d, expected=%d).\n", + type, nla_len(a), ovs_tunnel_key_lens[type]); + return -EINVAL; + } switch (type) { case OVS_TUNNEL_KEY_ATTR_ID: - tun_key->tun_id = nla_get_be64(a); - tun_key->tun_flags |= TUNNEL_KEY; + SW_FLOW_KEY_PUT(match, tun_key.tun_id, + nla_get_be64(a), is_mask); + tun_flags |= TUNNEL_KEY; break; case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: - tun_key->ipv4_src = nla_get_be32(a); + SW_FLOW_KEY_PUT(match, tun_key.ipv4_src, + nla_get_be32(a), is_mask); break; case OVS_TUNNEL_KEY_ATTR_IPV4_DST: - tun_key->ipv4_dst = nla_get_be32(a); + SW_FLOW_KEY_PUT(match, tun_key.ipv4_dst, + nla_get_be32(a), is_mask); break; case OVS_TUNNEL_KEY_ATTR_TOS: - tun_key->ipv4_tos = nla_get_u8(a); + SW_FLOW_KEY_PUT(match, tun_key.ipv4_tos, + nla_get_u8(a), is_mask); break; case OVS_TUNNEL_KEY_ATTR_TTL: - tun_key->ipv4_ttl = nla_get_u8(a); + SW_FLOW_KEY_PUT(match, tun_key.ipv4_ttl, + nla_get_u8(a), is_mask); ttl = true; break; case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: - tun_key->tun_flags |= TUNNEL_DONT_FRAGMENT; + tun_flags |= TUNNEL_DONT_FRAGMENT; break; case OVS_TUNNEL_KEY_ATTR_CSUM: - tun_key->tun_flags |= TUNNEL_CSUM; + tun_flags |= TUNNEL_CSUM; break; default: return -EINVAL; - } } - if (rem > 0) - return -EINVAL; - if (!tun_key->ipv4_dst) - return -EINVAL; + SW_FLOW_KEY_PUT(match, tun_key.tun_flags, tun_flags, is_mask); - if (!ttl) + if (rem > 0) { + OVS_NLERR("IPv4 tunnel attribute has %d unknown bytes.\n", rem); return -EINVAL; + } + + if (!is_mask) { + if (!match->key->tun_key.ipv4_dst) { + OVS_NLERR("IPv4 tunnel destination address is zero.\n"); + return -EINVAL; + } + + if (!ttl) { + OVS_NLERR("IPv4 tunnel TTL not specified.\n"); + return -EINVAL; + } + } return 0; } int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb, - const struct ovs_key_ipv4_tunnel *tun_key) + const struct ovs_key_ipv4_tunnel *tun_key, + const struct ovs_key_ipv4_tunnel *output) { struct nlattr *nla; @@ -1054,23 +1264,24 @@ int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb, if (!nla) return -EMSGSIZE; - if (tun_key->tun_flags & TUNNEL_KEY && - nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, tun_key->tun_id)) + if (output->tun_flags & TUNNEL_KEY && + nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id)) return -EMSGSIZE; - if (tun_key->ipv4_src && - nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tun_key->ipv4_src)) + if (output->ipv4_src && + nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->ipv4_src)) return -EMSGSIZE; - if (nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tun_key->ipv4_dst)) + if (output->ipv4_dst && + nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->ipv4_dst)) return -EMSGSIZE; - if (tun_key->ipv4_tos && - nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, tun_key->ipv4_tos)) + if (output->ipv4_tos && + nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->ipv4_tos)) return -EMSGSIZE; - if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, tun_key->ipv4_ttl)) + if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, output->ipv4_ttl)) return -EMSGSIZE; - if ((tun_key->tun_flags & TUNNEL_DONT_FRAGMENT) && + if ((output->tun_flags & TUNNEL_DONT_FRAGMENT) && nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT)) return -EMSGSIZE; - if ((tun_key->tun_flags & TUNNEL_CSUM) && + if ((output->tun_flags & TUNNEL_CSUM) && nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM)) return -EMSGSIZE; @@ -1078,176 +1289,372 @@ int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb, return 0; } -/** - * ovs_flow_from_nlattrs - parses Netlink attributes into a flow key. - * @swkey: receives the extracted flow key. - * @key_lenp: number of bytes used in @swkey. - * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute - * sequence. - */ -int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, - const struct nlattr *attr) +static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, + const struct nlattr **a, bool is_mask) { - const struct nlattr *a[OVS_KEY_ATTR_MAX + 1]; - const struct ovs_key_ethernet *eth_key; - int key_len; - u32 attrs; - int err; + if (*attrs & (1 << OVS_KEY_ATTR_PRIORITY)) { + SW_FLOW_KEY_PUT(match, phy.priority, + nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]), is_mask); + *attrs &= ~(1 << OVS_KEY_ATTR_PRIORITY); + } - memset(swkey, 0, sizeof(struct sw_flow_key)); - key_len = SW_FLOW_KEY_OFFSET(eth); + if (*attrs & (1 << OVS_KEY_ATTR_IN_PORT)) { + u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]); - err = parse_flow_nlattrs(attr, a, &attrs); - if (err) - return err; + if (is_mask) + in_port = 0xffffffff; /* Always exact match in_port. */ + else if (in_port >= DP_MAX_PORTS) + return -EINVAL; - /* Metadata attributes. */ - if (attrs & (1 << OVS_KEY_ATTR_PRIORITY)) { - swkey->phy.priority = nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]); - attrs &= ~(1 << OVS_KEY_ATTR_PRIORITY); + SW_FLOW_KEY_PUT(match, phy.in_port, in_port, is_mask); + *attrs &= ~(1 << OVS_KEY_ATTR_IN_PORT); + } else if (!is_mask) { + SW_FLOW_KEY_PUT(match, phy.in_port, DP_MAX_PORTS, is_mask); } - if (attrs & (1 << OVS_KEY_ATTR_IN_PORT)) { - u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]); - if (in_port >= DP_MAX_PORTS) - return -EINVAL; - swkey->phy.in_port = in_port; - attrs &= ~(1 << OVS_KEY_ATTR_IN_PORT); - } else { - swkey->phy.in_port = DP_MAX_PORTS; + + if (*attrs & (1 << OVS_KEY_ATTR_SKB_MARK)) { + uint32_t mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]); + + SW_FLOW_KEY_PUT(match, phy.skb_mark, mark, is_mask); + *attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK); } - if (attrs & (1 << OVS_KEY_ATTR_SKB_MARK)) { - swkey->phy.skb_mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]); - attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK); + if (*attrs & (1 << OVS_KEY_ATTR_TUNNEL)) { + if (ovs_ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match, + is_mask)) + return -EINVAL; + *attrs &= ~(1 << OVS_KEY_ATTR_TUNNEL); } + return 0; +} - if (attrs & (1 << OVS_KEY_ATTR_TUNNEL)) { - err = ovs_ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], &swkey->tun_key); - if (err) - return err; +static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, + const struct nlattr **a, bool is_mask) +{ + int err; + u64 orig_attrs = attrs; - attrs &= ~(1 << OVS_KEY_ATTR_TUNNEL); - } + err = metadata_from_nlattrs(match, &attrs, a, is_mask); + if (err) + return err; - /* Data attributes. */ - if (!(attrs & (1 << OVS_KEY_ATTR_ETHERNET))) - return -EINVAL; - attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET); + if (attrs & (1 << OVS_KEY_ATTR_ETHERNET)) { + const struct ovs_key_ethernet *eth_key; - eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]); - memcpy(swkey->eth.src, eth_key->eth_src, ETH_ALEN); - memcpy(swkey->eth.dst, eth_key->eth_dst, ETH_ALEN); + eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]); + SW_FLOW_KEY_MEMCPY(match, eth.src, + eth_key->eth_src, ETH_ALEN, is_mask); + SW_FLOW_KEY_MEMCPY(match, eth.dst, + eth_key->eth_dst, ETH_ALEN, is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET); + } - if (attrs & (1u << OVS_KEY_ATTR_ETHERTYPE) && - nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q)) { - const struct nlattr *encap; + if (attrs & (1 << OVS_KEY_ATTR_VLAN)) { __be16 tci; - if (attrs != ((1 << OVS_KEY_ATTR_VLAN) | - (1 << OVS_KEY_ATTR_ETHERTYPE) | - (1 << OVS_KEY_ATTR_ENCAP))) - return -EINVAL; - - encap = a[OVS_KEY_ATTR_ENCAP]; tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); - if (tci & htons(VLAN_TAG_PRESENT)) { - swkey->eth.tci = tci; - - err = parse_flow_nlattrs(encap, a, &attrs); - if (err) - return err; - } else if (!tci) { - /* Corner case for truncated 802.1Q header. */ - if (nla_len(encap)) - return -EINVAL; + if (!(tci & htons(VLAN_TAG_PRESENT))) { + if (is_mask) + OVS_NLERR("VLAN TCI mask does not have exact match for VLAN_TAG_PRESENT bit.\n"); + else + OVS_NLERR("VLAN TCI does not have VLAN_TAG_PRESENT bit set.\n"); - swkey->eth.type = htons(ETH_P_8021Q); - *key_lenp = key_len; - return 0; - } else { return -EINVAL; } - } + + SW_FLOW_KEY_PUT(match, eth.tci, tci, is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_VLAN); + } else if (!is_mask) + SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true); if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) { - swkey->eth.type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]); - if (ntohs(swkey->eth.type) < ETH_P_802_3_MIN) + __be16 eth_type; + + eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]); + if (is_mask) { + /* Always exact match EtherType. */ + eth_type = htons(0xffff); + } else if (ntohs(eth_type) < ETH_P_802_3_MIN) { + OVS_NLERR("EtherType is less than minimum (type=%x, min=%x).\n", + ntohs(eth_type), ETH_P_802_3_MIN); return -EINVAL; + } + + SW_FLOW_KEY_PUT(match, eth.type, eth_type, is_mask); attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); - } else { - swkey->eth.type = htons(ETH_P_802_2); + } else if (!is_mask) { + SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask); } - if (swkey->eth.type == htons(ETH_P_IP)) { + if (attrs & (1 << OVS_KEY_ATTR_IPV4)) { const struct ovs_key_ipv4 *ipv4_key; - if (!(attrs & (1 << OVS_KEY_ATTR_IPV4))) - return -EINVAL; - attrs &= ~(1 << OVS_KEY_ATTR_IPV4); - - key_len = SW_FLOW_KEY_OFFSET(ipv4.addr); ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]); - if (ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX) + if (!is_mask && ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX) { + OVS_NLERR("Unknown IPv4 fragment type (value=%d, max=%d).\n", + ipv4_key->ipv4_frag, OVS_FRAG_TYPE_MAX); return -EINVAL; - swkey->ip.proto = ipv4_key->ipv4_proto; - swkey->ip.tos = ipv4_key->ipv4_tos; - swkey->ip.ttl = ipv4_key->ipv4_ttl; - swkey->ip.frag = ipv4_key->ipv4_frag; - swkey->ipv4.addr.src = ipv4_key->ipv4_src; - swkey->ipv4.addr.dst = ipv4_key->ipv4_dst; - - if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) { - err = ipv4_flow_from_nlattrs(swkey, &key_len, a, &attrs); - if (err) - return err; } - } else if (swkey->eth.type == htons(ETH_P_IPV6)) { - const struct ovs_key_ipv6 *ipv6_key; + SW_FLOW_KEY_PUT(match, ip.proto, + ipv4_key->ipv4_proto, is_mask); + SW_FLOW_KEY_PUT(match, ip.tos, + ipv4_key->ipv4_tos, is_mask); + SW_FLOW_KEY_PUT(match, ip.ttl, + ipv4_key->ipv4_ttl, is_mask); + SW_FLOW_KEY_PUT(match, ip.frag, + ipv4_key->ipv4_frag, is_mask); + SW_FLOW_KEY_PUT(match, ipv4.addr.src, + ipv4_key->ipv4_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv4.addr.dst, + ipv4_key->ipv4_dst, is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_IPV4); + } - if (!(attrs & (1 << OVS_KEY_ATTR_IPV6))) - return -EINVAL; - attrs &= ~(1 << OVS_KEY_ATTR_IPV6); + if (attrs & (1 << OVS_KEY_ATTR_IPV6)) { + const struct ovs_key_ipv6 *ipv6_key; - key_len = SW_FLOW_KEY_OFFSET(ipv6.label); ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]); - if (ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX) + if (!is_mask && ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX) { + OVS_NLERR("Unknown IPv6 fragment type (value=%d, max=%d).\n", + ipv6_key->ipv6_frag, OVS_FRAG_TYPE_MAX); return -EINVAL; - swkey->ipv6.label = ipv6_key->ipv6_label; - swkey->ip.proto = ipv6_key->ipv6_proto; - swkey->ip.tos = ipv6_key->ipv6_tclass; - swkey->ip.ttl = ipv6_key->ipv6_hlimit; - swkey->ip.frag = ipv6_key->ipv6_frag; - memcpy(&swkey->ipv6.addr.src, ipv6_key->ipv6_src, - sizeof(swkey->ipv6.addr.src)); - memcpy(&swkey->ipv6.addr.dst, ipv6_key->ipv6_dst, - sizeof(swkey->ipv6.addr.dst)); - - if (swkey->ip.frag != OVS_FRAG_TYPE_LATER) { - err = ipv6_flow_from_nlattrs(swkey, &key_len, a, &attrs); - if (err) - return err; } - } else if (swkey->eth.type == htons(ETH_P_ARP) || - swkey->eth.type == htons(ETH_P_RARP)) { + SW_FLOW_KEY_PUT(match, ipv6.label, + ipv6_key->ipv6_label, is_mask); + SW_FLOW_KEY_PUT(match, ip.proto, + ipv6_key->ipv6_proto, is_mask); + SW_FLOW_KEY_PUT(match, ip.tos, + ipv6_key->ipv6_tclass, is_mask); + SW_FLOW_KEY_PUT(match, ip.ttl, + ipv6_key->ipv6_hlimit, is_mask); + SW_FLOW_KEY_PUT(match, ip.frag, + ipv6_key->ipv6_frag, is_mask); + SW_FLOW_KEY_MEMCPY(match, ipv6.addr.src, + ipv6_key->ipv6_src, + sizeof(match->key->ipv6.addr.src), + is_mask); + SW_FLOW_KEY_MEMCPY(match, ipv6.addr.dst, + ipv6_key->ipv6_dst, + sizeof(match->key->ipv6.addr.dst), + is_mask); + + attrs &= ~(1 << OVS_KEY_ATTR_IPV6); + } + + if (attrs & (1 << OVS_KEY_ATTR_ARP)) { const struct ovs_key_arp *arp_key; - if (!(attrs & (1 << OVS_KEY_ATTR_ARP))) + arp_key = nla_data(a[OVS_KEY_ATTR_ARP]); + if (!is_mask && (arp_key->arp_op & htons(0xff00))) { + OVS_NLERR("Unknown ARP opcode (opcode=%d).\n", + arp_key->arp_op); return -EINVAL; + } + + SW_FLOW_KEY_PUT(match, ipv4.addr.src, + arp_key->arp_sip, is_mask); + SW_FLOW_KEY_PUT(match, ipv4.addr.dst, + arp_key->arp_tip, is_mask); + SW_FLOW_KEY_PUT(match, ip.proto, + ntohs(arp_key->arp_op), is_mask); + SW_FLOW_KEY_MEMCPY(match, ipv4.arp.sha, + arp_key->arp_sha, ETH_ALEN, is_mask); + SW_FLOW_KEY_MEMCPY(match, ipv4.arp.tha, + arp_key->arp_tha, ETH_ALEN, is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_ARP); + } - key_len = SW_FLOW_KEY_OFFSET(ipv4.arp); - arp_key = nla_data(a[OVS_KEY_ATTR_ARP]); - swkey->ipv4.addr.src = arp_key->arp_sip; - swkey->ipv4.addr.dst = arp_key->arp_tip; - if (arp_key->arp_op & htons(0xff00)) + if (attrs & (1 << OVS_KEY_ATTR_TCP)) { + const struct ovs_key_tcp *tcp_key; + + tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]); + if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { + SW_FLOW_KEY_PUT(match, ipv4.tp.src, + tcp_key->tcp_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv4.tp.dst, + tcp_key->tcp_dst, is_mask); + } else { + SW_FLOW_KEY_PUT(match, ipv6.tp.src, + tcp_key->tcp_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv6.tp.dst, + tcp_key->tcp_dst, is_mask); + } + attrs &= ~(1 << OVS_KEY_ATTR_TCP); + } + + if (attrs & (1 << OVS_KEY_ATTR_UDP)) { + const struct ovs_key_udp *udp_key; + + udp_key = nla_data(a[OVS_KEY_ATTR_UDP]); + if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { + SW_FLOW_KEY_PUT(match, ipv4.tp.src, + udp_key->udp_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv4.tp.dst, + udp_key->udp_dst, is_mask); + } else { + SW_FLOW_KEY_PUT(match, ipv6.tp.src, + udp_key->udp_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv6.tp.dst, + udp_key->udp_dst, is_mask); + } + attrs &= ~(1 << OVS_KEY_ATTR_UDP); + } + + if (attrs & (1 << OVS_KEY_ATTR_ICMP)) { + const struct ovs_key_icmp *icmp_key; + + icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]); + SW_FLOW_KEY_PUT(match, ipv4.tp.src, + htons(icmp_key->icmp_type), is_mask); + SW_FLOW_KEY_PUT(match, ipv4.tp.dst, + htons(icmp_key->icmp_code), is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_ICMP); + } + + if (attrs & (1 << OVS_KEY_ATTR_ICMPV6)) { + const struct ovs_key_icmpv6 *icmpv6_key; + + icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]); + SW_FLOW_KEY_PUT(match, ipv6.tp.src, + htons(icmpv6_key->icmpv6_type), is_mask); + SW_FLOW_KEY_PUT(match, ipv6.tp.dst, + htons(icmpv6_key->icmpv6_code), is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_ICMPV6); + } + + if (attrs & (1 << OVS_KEY_ATTR_ND)) { + const struct ovs_key_nd *nd_key; + + nd_key = nla_data(a[OVS_KEY_ATTR_ND]); + SW_FLOW_KEY_MEMCPY(match, ipv6.nd.target, + nd_key->nd_target, + sizeof(match->key->ipv6.nd.target), + is_mask); + SW_FLOW_KEY_MEMCPY(match, ipv6.nd.sll, + nd_key->nd_sll, ETH_ALEN, is_mask); + SW_FLOW_KEY_MEMCPY(match, ipv6.nd.tll, + nd_key->nd_tll, ETH_ALEN, is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_ND); + } + + if (attrs != 0) + return -EINVAL; + + return 0; +} + +/** + * ovs_match_from_nlattrs - parses Netlink attributes into a flow key and + * mask. In case the 'mask' is NULL, the flow is treated as exact match + * flow. Otherwise, it is treated as a wildcarded flow, except the mask + * does not include any don't care bit. + * @match: receives the extracted flow match information. + * @key: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute + * sequence. The fields should of the packet that triggered the creation + * of this flow. + * @mask: Optional. Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink + * attribute specifies the mask field of the wildcarded flow. + */ +int ovs_match_from_nlattrs(struct sw_flow_match *match, + const struct nlattr *key, + const struct nlattr *mask) +{ + const struct nlattr *a[OVS_KEY_ATTR_MAX + 1]; + const struct nlattr *encap; + u64 key_attrs = 0; + u64 mask_attrs = 0; + bool encap_valid = false; + int err; + + err = parse_flow_nlattrs(key, a, &key_attrs); + if (err) + return err; + + if ((key_attrs & (1 << OVS_KEY_ATTR_ETHERNET)) && + (key_attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) && + (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q))) { + __be16 tci; + + if (!((key_attrs & (1 << OVS_KEY_ATTR_VLAN)) && + (key_attrs & (1 << OVS_KEY_ATTR_ENCAP)))) { + OVS_NLERR("Invalid Vlan frame.\n"); return -EINVAL; - swkey->ip.proto = ntohs(arp_key->arp_op); - memcpy(swkey->ipv4.arp.sha, arp_key->arp_sha, ETH_ALEN); - memcpy(swkey->ipv4.arp.tha, arp_key->arp_tha, ETH_ALEN); + } + + key_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); + tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); + encap = a[OVS_KEY_ATTR_ENCAP]; + key_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP); + encap_valid = true; + + if (tci & htons(VLAN_TAG_PRESENT)) { + err = parse_flow_nlattrs(encap, a, &key_attrs); + if (err) + return err; + } else if (!tci) { + /* Corner case for truncated 802.1Q header. */ + if (nla_len(encap)) { + OVS_NLERR("Truncated 802.1Q header has non-zero encap attribute.\n"); + return -EINVAL; + } + } else { + OVS_NLERR("Encap attribute is set for a non-VLAN frame.\n"); + return -EINVAL; + } } - if (attrs) + err = ovs_key_from_nlattrs(match, key_attrs, a, false); + if (err) + return err; + + if (mask) { + err = parse_flow_mask_nlattrs(mask, a, &mask_attrs); + if (err) + return err; + + if (mask_attrs & 1ULL << OVS_KEY_ATTR_ENCAP) { + __be16 eth_type = 0; + __be16 tci = 0; + + if (!encap_valid) { + OVS_NLERR("Encap mask attribute is set for non-VLAN frame.\n"); + return -EINVAL; + } + + mask_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP); + if (a[OVS_KEY_ATTR_ETHERTYPE]) + eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]); + + if (eth_type == htons(0xffff)) { + mask_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); + encap = a[OVS_KEY_ATTR_ENCAP]; + err = parse_flow_mask_nlattrs(encap, a, &mask_attrs); + } else { + OVS_NLERR("VLAN frames must have an exact match on the TPID (mask=%x).\n", + ntohs(eth_type)); + return -EINVAL; + } + + if (a[OVS_KEY_ATTR_VLAN]) + tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); + + if (!(tci & htons(VLAN_TAG_PRESENT))) { + OVS_NLERR("VLAN tag present bit must have an exact match (tci_mask=%x).\n", ntohs(tci)); + return -EINVAL; + } + } + + err = ovs_key_from_nlattrs(match, mask_attrs, a, true); + if (err) + return err; + } else { + /* Populate exact match flow's key mask. */ + if (match->mask) + ovs_sw_flow_mask_set(match->mask, &match->range, 0xff); + } + + if (!ovs_match_validate(match, key_attrs, mask_attrs)) return -EINVAL; - *key_lenp = key_len; return 0; } @@ -1255,7 +1662,6 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, /** * ovs_flow_metadata_from_nlattrs - parses Netlink attributes into a flow key. * @flow: Receives extracted in_port, priority, tun_key and skb_mark. - * @key_len: Length of key in @flow. Used for calculating flow hash. * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute * sequence. * @@ -1264,102 +1670,100 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, * get the metadata, that is, the parts of the flow key that cannot be * extracted from the packet itself. */ -int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len, - const struct nlattr *attr) + +int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, + const struct nlattr *attr) { struct ovs_key_ipv4_tunnel *tun_key = &flow->key.tun_key; - const struct nlattr *nla; - int rem; + const struct nlattr *a[OVS_KEY_ATTR_MAX + 1]; + u64 attrs = 0; + int err; + struct sw_flow_match match; flow->key.phy.in_port = DP_MAX_PORTS; flow->key.phy.priority = 0; flow->key.phy.skb_mark = 0; memset(tun_key, 0, sizeof(flow->key.tun_key)); - nla_for_each_nested(nla, attr, rem) { - int type = nla_type(nla); - - if (type <= OVS_KEY_ATTR_MAX && ovs_key_lens[type] > 0) { - int err; - - if (nla_len(nla) != ovs_key_lens[type]) - return -EINVAL; - - switch (type) { - case OVS_KEY_ATTR_PRIORITY: - flow->key.phy.priority = nla_get_u32(nla); - break; - - case OVS_KEY_ATTR_TUNNEL: - err = ovs_ipv4_tun_from_nlattr(nla, tun_key); - if (err) - return err; - break; - - case OVS_KEY_ATTR_IN_PORT: - if (nla_get_u32(nla) >= DP_MAX_PORTS) - return -EINVAL; - flow->key.phy.in_port = nla_get_u32(nla); - break; - - case OVS_KEY_ATTR_SKB_MARK: - flow->key.phy.skb_mark = nla_get_u32(nla); - break; - } - } - } - if (rem) + err = parse_flow_nlattrs(attr, a, &attrs); + if (err) return -EINVAL; - flow->hash = ovs_flow_hash(&flow->key, - flow_key_start(&flow->key), key_len); + memset(&match, 0, sizeof(match)); + match.key = &flow->key; + + err = metadata_from_nlattrs(&match, &attrs, a, false); + if (err) + return err; return 0; } -int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) +int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, + const struct sw_flow_key *output, struct sk_buff *skb) { struct ovs_key_ethernet *eth_key; struct nlattr *nla, *encap; + bool is_mask = (swkey != output); - if (swkey->phy.priority && - nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, swkey->phy.priority)) + if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority)) goto nla_put_failure; - if (swkey->tun_key.ipv4_dst && - ovs_ipv4_tun_to_nlattr(skb, &swkey->tun_key)) + if ((swkey->tun_key.ipv4_dst || is_mask) && + ovs_ipv4_tun_to_nlattr(skb, &swkey->tun_key, &output->tun_key)) goto nla_put_failure; - if (swkey->phy.in_port != DP_MAX_PORTS && - nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, swkey->phy.in_port)) - goto nla_put_failure; + if (swkey->phy.in_port == DP_MAX_PORTS) { + if (is_mask && (output->phy.in_port == 0xffff)) + if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, 0xffffffff)) + goto nla_put_failure; + } else { + u16 upper_u16; + upper_u16 = !is_mask ? 0 : 0xffff; - if (swkey->phy.skb_mark && - nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, swkey->phy.skb_mark)) + if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, + (upper_u16 << 16) | output->phy.in_port)) + goto nla_put_failure; + } + + if (nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, output->phy.skb_mark)) goto nla_put_failure; nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key)); if (!nla) goto nla_put_failure; + eth_key = nla_data(nla); - memcpy(eth_key->eth_src, swkey->eth.src, ETH_ALEN); - memcpy(eth_key->eth_dst, swkey->eth.dst, ETH_ALEN); + memcpy(eth_key->eth_src, output->eth.src, ETH_ALEN); + memcpy(eth_key->eth_dst, output->eth.dst, ETH_ALEN); if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) { - if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_P_8021Q)) || - nla_put_be16(skb, OVS_KEY_ATTR_VLAN, swkey->eth.tci)) + __be16 eth_type; + eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff); + if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) || + nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci)) goto nla_put_failure; encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP); if (!swkey->eth.tci) goto unencap; - } else { + } else encap = NULL; - } - if (swkey->eth.type == htons(ETH_P_802_2)) + if (swkey->eth.type == htons(ETH_P_802_2)) { + /* + * Ethertype 802.2 is represented in the netlink with omitted + * OVS_KEY_ATTR_ETHERTYPE in the flow key attribute, and + * 0xffff in the mask attribute. Ethertype can also + * be wildcarded. + */ + if (is_mask && output->eth.type) + if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, + output->eth.type)) + goto nla_put_failure; goto unencap; + } - if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, swkey->eth.type)) + if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type)) goto nla_put_failure; if (swkey->eth.type == htons(ETH_P_IP)) { @@ -1369,12 +1773,12 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) if (!nla) goto nla_put_failure; ipv4_key = nla_data(nla); - ipv4_key->ipv4_src = swkey->ipv4.addr.src; - ipv4_key->ipv4_dst = swkey->ipv4.addr.dst; - ipv4_key->ipv4_proto = swkey->ip.proto; - ipv4_key->ipv4_tos = swkey->ip.tos; - ipv4_key->ipv4_ttl = swkey->ip.ttl; - ipv4_key->ipv4_frag = swkey->ip.frag; + ipv4_key->ipv4_src = output->ipv4.addr.src; + ipv4_key->ipv4_dst = output->ipv4.addr.dst; + ipv4_key->ipv4_proto = output->ip.proto; + ipv4_key->ipv4_tos = output->ip.tos; + ipv4_key->ipv4_ttl = output->ip.ttl; + ipv4_key->ipv4_frag = output->ip.frag; } else if (swkey->eth.type == htons(ETH_P_IPV6)) { struct ovs_key_ipv6 *ipv6_key; @@ -1382,15 +1786,15 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) if (!nla) goto nla_put_failure; ipv6_key = nla_data(nla); - memcpy(ipv6_key->ipv6_src, &swkey->ipv6.addr.src, + memcpy(ipv6_key->ipv6_src, &output->ipv6.addr.src, sizeof(ipv6_key->ipv6_src)); - memcpy(ipv6_key->ipv6_dst, &swkey->ipv6.addr.dst, + memcpy(ipv6_key->ipv6_dst, &output->ipv6.addr.dst, sizeof(ipv6_key->ipv6_dst)); - ipv6_key->ipv6_label = swkey->ipv6.label; - ipv6_key->ipv6_proto = swkey->ip.proto; - ipv6_key->ipv6_tclass = swkey->ip.tos; - ipv6_key->ipv6_hlimit = swkey->ip.ttl; - ipv6_key->ipv6_frag = swkey->ip.frag; + ipv6_key->ipv6_label = output->ipv6.label; + ipv6_key->ipv6_proto = output->ip.proto; + ipv6_key->ipv6_tclass = output->ip.tos; + ipv6_key->ipv6_hlimit = output->ip.ttl; + ipv6_key->ipv6_frag = output->ip.frag; } else if (swkey->eth.type == htons(ETH_P_ARP) || swkey->eth.type == htons(ETH_P_RARP)) { struct ovs_key_arp *arp_key; @@ -1400,11 +1804,11 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) goto nla_put_failure; arp_key = nla_data(nla); memset(arp_key, 0, sizeof(struct ovs_key_arp)); - arp_key->arp_sip = swkey->ipv4.addr.src; - arp_key->arp_tip = swkey->ipv4.addr.dst; - arp_key->arp_op = htons(swkey->ip.proto); - memcpy(arp_key->arp_sha, swkey->ipv4.arp.sha, ETH_ALEN); - memcpy(arp_key->arp_tha, swkey->ipv4.arp.tha, ETH_ALEN); + arp_key->arp_sip = output->ipv4.addr.src; + arp_key->arp_tip = output->ipv4.addr.dst; + arp_key->arp_op = htons(output->ip.proto); + memcpy(arp_key->arp_sha, output->ipv4.arp.sha, ETH_ALEN); + memcpy(arp_key->arp_tha, output->ipv4.arp.tha, ETH_ALEN); } if ((swkey->eth.type == htons(ETH_P_IP) || @@ -1419,11 +1823,11 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) goto nla_put_failure; tcp_key = nla_data(nla); if (swkey->eth.type == htons(ETH_P_IP)) { - tcp_key->tcp_src = swkey->ipv4.tp.src; - tcp_key->tcp_dst = swkey->ipv4.tp.dst; + tcp_key->tcp_src = output->ipv4.tp.src; + tcp_key->tcp_dst = output->ipv4.tp.dst; } else if (swkey->eth.type == htons(ETH_P_IPV6)) { - tcp_key->tcp_src = swkey->ipv6.tp.src; - tcp_key->tcp_dst = swkey->ipv6.tp.dst; + tcp_key->tcp_src = output->ipv6.tp.src; + tcp_key->tcp_dst = output->ipv6.tp.dst; } } else if (swkey->ip.proto == IPPROTO_UDP) { struct ovs_key_udp *udp_key; @@ -1433,11 +1837,11 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) goto nla_put_failure; udp_key = nla_data(nla); if (swkey->eth.type == htons(ETH_P_IP)) { - udp_key->udp_src = swkey->ipv4.tp.src; - udp_key->udp_dst = swkey->ipv4.tp.dst; + udp_key->udp_src = output->ipv4.tp.src; + udp_key->udp_dst = output->ipv4.tp.dst; } else if (swkey->eth.type == htons(ETH_P_IPV6)) { - udp_key->udp_src = swkey->ipv6.tp.src; - udp_key->udp_dst = swkey->ipv6.tp.dst; + udp_key->udp_src = output->ipv6.tp.src; + udp_key->udp_dst = output->ipv6.tp.dst; } } else if (swkey->eth.type == htons(ETH_P_IP) && swkey->ip.proto == IPPROTO_ICMP) { @@ -1447,8 +1851,8 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) if (!nla) goto nla_put_failure; icmp_key = nla_data(nla); - icmp_key->icmp_type = ntohs(swkey->ipv4.tp.src); - icmp_key->icmp_code = ntohs(swkey->ipv4.tp.dst); + icmp_key->icmp_type = ntohs(output->ipv4.tp.src); + icmp_key->icmp_code = ntohs(output->ipv4.tp.dst); } else if (swkey->eth.type == htons(ETH_P_IPV6) && swkey->ip.proto == IPPROTO_ICMPV6) { struct ovs_key_icmpv6 *icmpv6_key; @@ -1458,8 +1862,8 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) if (!nla) goto nla_put_failure; icmpv6_key = nla_data(nla); - icmpv6_key->icmpv6_type = ntohs(swkey->ipv6.tp.src); - icmpv6_key->icmpv6_code = ntohs(swkey->ipv6.tp.dst); + icmpv6_key->icmpv6_type = ntohs(output->ipv6.tp.src); + icmpv6_key->icmpv6_code = ntohs(output->ipv6.tp.dst); if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION || icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) { @@ -1469,10 +1873,10 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) if (!nla) goto nla_put_failure; nd_key = nla_data(nla); - memcpy(nd_key->nd_target, &swkey->ipv6.nd.target, + memcpy(nd_key->nd_target, &output->ipv6.nd.target, sizeof(nd_key->nd_target)); - memcpy(nd_key->nd_sll, swkey->ipv6.nd.sll, ETH_ALEN); - memcpy(nd_key->nd_tll, swkey->ipv6.nd.tll, ETH_ALEN); + memcpy(nd_key->nd_sll, output->ipv6.nd.sll, ETH_ALEN); + memcpy(nd_key->nd_tll, output->ipv6.nd.tll, ETH_ALEN); } } } @@ -1504,3 +1908,84 @@ void ovs_flow_exit(void) { kmem_cache_destroy(flow_cache); } + +struct sw_flow_mask *ovs_sw_flow_mask_alloc(void) +{ + struct sw_flow_mask *mask; + + mask = kmalloc(sizeof(*mask), GFP_KERNEL); + if (mask) + mask->ref_count = 0; + + return mask; +} + +void ovs_sw_flow_mask_add_ref(struct sw_flow_mask *mask) +{ + mask->ref_count++; +} + +void ovs_sw_flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred) +{ + if (!mask) + return; + + BUG_ON(!mask->ref_count); + mask->ref_count--; + + if (!mask->ref_count) { + list_del_rcu(&mask->list); + if (deferred) + kfree_rcu(mask, rcu); + else + kfree(mask); + } +} + +static bool ovs_sw_flow_mask_equal(const struct sw_flow_mask *a, + const struct sw_flow_mask *b) +{ + u8 *a_ = (u8 *)&a->key + a->range.start; + u8 *b_ = (u8 *)&b->key + b->range.start; + + return (a->range.end == b->range.end) + && (a->range.start == b->range.start) + && (memcmp(a_, b_, ovs_sw_flow_mask_actual_size(a)) == 0); +} + +struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *tbl, + const struct sw_flow_mask *mask) +{ + struct list_head *ml; + + list_for_each(ml, tbl->mask_list) { + struct sw_flow_mask *m; + m = container_of(ml, struct sw_flow_mask, list); + if (ovs_sw_flow_mask_equal(mask, m)) + return m; + } + + return NULL; +} + +/** + * add a new mask into the mask list. + * The caller needs to make sure that 'mask' is not the same + * as any masks that are already on the list. + */ +void ovs_sw_flow_mask_insert(struct flow_table *tbl, struct sw_flow_mask *mask) +{ + list_add_rcu(&mask->list, tbl->mask_list); +} + +/** + * Set 'range' fields in the mask to the value of 'val'. + */ +static void ovs_sw_flow_mask_set(struct sw_flow_mask *mask, + struct sw_flow_key_range *range, u8 val) +{ + u8 *m = (u8 *)&mask->key + range->start; + + mask->range = *range; + memset(m, val, ovs_sw_flow_mask_size_roundup(mask)); +} diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index 66ef7220293e..9674e45f6969 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2011 Nicira, Inc. + * Copyright (c) 2007-2013 Nicira, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -33,6 +33,8 @@ #include struct sk_buff; +struct sw_flow_mask; +struct flow_table; struct sw_flow_actions { struct rcu_head rcu; @@ -131,6 +133,8 @@ struct sw_flow { u32 hash; struct sw_flow_key key; + struct sw_flow_key unmasked_key; + struct sw_flow_mask *mask; struct sw_flow_actions __rcu *sf_acts; spinlock_t lock; /* Lock for values below. */ @@ -140,6 +144,25 @@ struct sw_flow { u8 tcp_flags; /* Union of seen TCP flags. */ }; +struct sw_flow_key_range { + size_t start; + size_t end; +}; + +static inline u16 ovs_sw_flow_key_range_actual_size(const struct sw_flow_key_range *range) +{ + return range->end - range->start; +} + +struct sw_flow_match { + struct sw_flow_key *key; + struct sw_flow_key_range range; + struct sw_flow_mask *mask; +}; + +void ovs_match_init(struct sw_flow_match *match, + struct sw_flow_key *key, struct sw_flow_mask *mask); + struct arp_eth_header { __be16 ar_hrd; /* format of hardware address */ __be16 ar_pro; /* format of protocol address */ @@ -159,21 +182,21 @@ void ovs_flow_exit(void); struct sw_flow *ovs_flow_alloc(void); void ovs_flow_deferred_free(struct sw_flow *); -void ovs_flow_free(struct sw_flow *flow); +void ovs_flow_free(struct sw_flow *, bool deferred); struct sw_flow_actions *ovs_flow_actions_alloc(int actions_len); void ovs_flow_deferred_free_acts(struct sw_flow_actions *); -int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *, - int *key_lenp); +int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *); void ovs_flow_used(struct sw_flow *, struct sk_buff *); u64 ovs_flow_used_time(unsigned long flow_jiffies); - -int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *); -int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, +int ovs_flow_to_nlattrs(const struct sw_flow_key *, + const struct sw_flow_key *, struct sk_buff *); +int ovs_match_from_nlattrs(struct sw_flow_match *match, + const struct nlattr *, const struct nlattr *); -int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len, - const struct nlattr *attr); +int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, + const struct nlattr *attr); #define MAX_ACTIONS_BUFSIZE (32 * 1024) #define TBL_MIN_BUCKETS 1024 @@ -182,6 +205,7 @@ struct flow_table { struct flex_array *buckets; unsigned int count, n_buckets; struct rcu_head rcu; + struct list_head *mask_list; int node_ver; u32 hash_seed; bool keep_flows; @@ -197,22 +221,56 @@ static inline int ovs_flow_tbl_need_to_expand(struct flow_table *table) return (table->count > table->n_buckets); } -struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *table, - struct sw_flow_key *key, int len); -void ovs_flow_tbl_destroy(struct flow_table *table); -void ovs_flow_tbl_deferred_destroy(struct flow_table *table); +struct sw_flow *ovs_flow_lookup(struct flow_table *, + const struct sw_flow_key *); +struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table, + struct sw_flow_match *match); + +void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred); struct flow_table *ovs_flow_tbl_alloc(int new_size); struct flow_table *ovs_flow_tbl_expand(struct flow_table *table); struct flow_table *ovs_flow_tbl_rehash(struct flow_table *table); -void ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow, - struct sw_flow_key *key, int key_len); -void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow); -struct sw_flow *ovs_flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *idx); +void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow); +void ovs_flow_remove(struct flow_table *table, struct sw_flow *flow); + +struct sw_flow *ovs_flow_dump_next(struct flow_table *table, u32 *bucket, u32 *idx); extern const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1]; int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr, - struct ovs_key_ipv4_tunnel *tun_key); + struct sw_flow_match *match, bool is_mask); int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb, - const struct ovs_key_ipv4_tunnel *tun_key); + const struct ovs_key_ipv4_tunnel *tun_key, + const struct ovs_key_ipv4_tunnel *output); + +bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, + const struct sw_flow_key *key, int key_len); + +struct sw_flow_mask { + int ref_count; + struct rcu_head rcu; + struct list_head list; + struct sw_flow_key_range range; + struct sw_flow_key key; +}; + +static inline u16 +ovs_sw_flow_mask_actual_size(const struct sw_flow_mask *mask) +{ + return ovs_sw_flow_key_range_actual_size(&mask->range); +} + +static inline u16 +ovs_sw_flow_mask_size_roundup(const struct sw_flow_mask *mask) +{ + return roundup(ovs_sw_flow_mask_actual_size(mask), sizeof(u32)); +} +struct sw_flow_mask *ovs_sw_flow_mask_alloc(void); +void ovs_sw_flow_mask_add_ref(struct sw_flow_mask *); +void ovs_sw_flow_mask_del_ref(struct sw_flow_mask *, bool deferred); +void ovs_sw_flow_mask_insert(struct flow_table *, struct sw_flow_mask *); +struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *, + const struct sw_flow_mask *); +void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src, + const struct sw_flow_mask *mask); #endif /* flow.h */ -- cgit v1.2.3-71-gd317 From 280c571e1a0f553d4f2384f1a8b643b5de26e0ec Mon Sep 17 00:00:00 2001 From: Joe Stringer Date: Tue, 23 Jul 2013 13:37:45 +0900 Subject: net: Add NEXTHDR_SCTP to ipv6.h Signed-off-by: Joe Stringer Signed-off-by: Jesse Gross --- include/net/ipv6.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 5fe564985171..7bdff0455065 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -41,6 +41,7 @@ #define NEXTHDR_ICMP 58 /* ICMP for IPv6. */ #define NEXTHDR_NONE 59 /* No next header */ #define NEXTHDR_DEST 60 /* Destination options header. */ +#define NEXTHDR_SCTP 132 /* SCTP message. */ #define NEXTHDR_MOBILITY 135 /* Mobility header. */ #define NEXTHDR_MAX 255 -- cgit v1.2.3-71-gd317 From d1d70aa69db3d09240e9faf6bf68a044b9107480 Mon Sep 17 00:00:00 2001 From: "Lad, Prabhakar" Date: Sun, 11 Aug 2013 02:02:24 -0300 Subject: [media] media: OF: add "sync-on-green-active" property This patch adds 'sync-on-green-active' property as part of endpoint property. Signed-off-by: Lad, Prabhakar Acked-by: Sylwester Nawrocki Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/video-interfaces.txt | 2 ++ drivers/media/v4l2-core/v4l2-of.c | 4 ++++ include/media/v4l2-mediabus.h | 3 +++ 3 files changed, 9 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt index e022d2dc4962..ce719f89dd1c 100644 --- a/Documentation/devicetree/bindings/media/video-interfaces.txt +++ b/Documentation/devicetree/bindings/media/video-interfaces.txt @@ -88,6 +88,8 @@ Optional endpoint properties - field-even-active: field signal level during the even field data transmission. - pclk-sample: sample data on rising (1) or falling (0) edge of the pixel clock signal. +- sync-on-green-active: active state of Sync-on-green (SoG) signal, 0/1 for + LOW/HIGH respectively. - data-lanes: an array of physical data lane indexes. Position of an entry determines the logical lane number, while the value of an entry indicates physical lane, e.g. for 2-lane MIPI CSI-2 bus we could have diff --git a/drivers/media/v4l2-core/v4l2-of.c b/drivers/media/v4l2-core/v4l2-of.c index ed305d8c65f0..a6478dca0cde 100644 --- a/drivers/media/v4l2-core/v4l2-of.c +++ b/drivers/media/v4l2-core/v4l2-of.c @@ -100,6 +100,10 @@ static void v4l2_of_parse_parallel_bus(const struct device_node *node, if (!of_property_read_u32(node, "data-shift", &v)) bus->data_shift = v; + if (!of_property_read_u32(node, "sync-on-green-active", &v)) + flags |= v ? V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH : + V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW; + bus->flags = flags; } diff --git a/include/media/v4l2-mediabus.h b/include/media/v4l2-mediabus.h index 83ae07e53350..395c4a95a42a 100644 --- a/include/media/v4l2-mediabus.h +++ b/include/media/v4l2-mediabus.h @@ -40,6 +40,9 @@ #define V4L2_MBUS_FIELD_EVEN_HIGH (1 << 10) /* FIELD = 1/0 - Field1 (odd)/Field2 (even) */ #define V4L2_MBUS_FIELD_EVEN_LOW (1 << 11) +/* Active state of Sync-on-green (SoG) signal, 0/1 for LOW/HIGH respectively. */ +#define V4L2_MBUS_VIDEO_SOG_ACTIVE_HIGH (1 << 12) +#define V4L2_MBUS_VIDEO_SOG_ACTIVE_LOW (1 << 13) /* Serial flags */ /* How many lanes the client can use */ -- cgit v1.2.3-71-gd317 From 0216dc2fe666b5cedba54df4c0be6ddc8eb352e1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 15 Aug 2013 08:02:40 -0300 Subject: [media] v4l2-dv-timings: add v4l2_print_dv_timings helper Drivers often have to log the contents of a dv_timings struct. Adding this helper will make it easier for drivers to do so. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dv-timings.c | 49 +++++++++++++++++++++++++++++++ include/media/v4l2-dv-timings.h | 9 ++++++ 2 files changed, 58 insertions(+) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 72cf2240bac4..917e58ce1f18 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -223,6 +223,55 @@ bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, } EXPORT_SYMBOL_GPL(v4l_match_dv_timings); +void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, + const struct v4l2_dv_timings *t, bool detailed) +{ + const struct v4l2_bt_timings *bt = &t->bt; + u32 htot, vtot; + + if (t->type != V4L2_DV_BT_656_1120) + return; + + htot = V4L2_DV_BT_FRAME_WIDTH(bt); + vtot = V4L2_DV_BT_FRAME_HEIGHT(bt); + + if (prefix == NULL) + prefix = ""; + + pr_info("%s: %s%ux%u%s%u (%ux%u)\n", dev_prefix, prefix, + bt->width, bt->height, bt->interlaced ? "i" : "p", + (htot * vtot) > 0 ? ((u32)bt->pixelclock / (htot * vtot)) : 0, + htot, vtot); + + if (!detailed) + return; + + pr_info("%s: horizontal: fp = %u, %ssync = %u, bp = %u\n", + dev_prefix, bt->hfrontporch, + (bt->polarities & V4L2_DV_HSYNC_POS_POL) ? "+" : "-", + bt->hsync, bt->hbackporch); + pr_info("%s: vertical: fp = %u, %ssync = %u, bp = %u\n", + dev_prefix, bt->vfrontporch, + (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-", + bt->vsync, bt->vbackporch); + pr_info("%s: pixelclock: %llu\n", dev_prefix, bt->pixelclock); + pr_info("%s: flags (0x%x):%s%s%s%s\n", dev_prefix, bt->flags, + (bt->flags & V4L2_DV_FL_REDUCED_BLANKING) ? + " REDUCED_BLANKING" : "", + (bt->flags & V4L2_DV_FL_CAN_REDUCE_FPS) ? + " CAN_REDUCE_FPS" : "", + (bt->flags & V4L2_DV_FL_REDUCED_FPS) ? + " REDUCED_FPS" : "", + (bt->flags & V4L2_DV_FL_HALF_LINE) ? + " HALF_LINE" : ""); + pr_info("%s: standards (0x%x):%s%s%s%s\n", dev_prefix, bt->standards, + (bt->standards & V4L2_DV_BT_STD_CEA861) ? " CEA" : "", + (bt->standards & V4L2_DV_BT_STD_DMT) ? " DMT" : "", + (bt->standards & V4L2_DV_BT_STD_CVT) ? " CVT" : "", + (bt->standards & V4L2_DV_BT_STD_GTF) ? " GTF" : ""); +} +EXPORT_SYMBOL_GPL(v4l2_print_dv_timings); + /* * CVT defines * Based on Coordinated Video Timings Standard diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 4c7bb5491658..696e5c2fd176 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -76,6 +76,15 @@ bool v4l_match_dv_timings(const struct v4l2_dv_timings *measured, const struct v4l2_dv_timings *standard, unsigned pclock_delta); +/** v4l2_print_dv_timings() - log the contents of a dv_timings struct + * @dev_prefix:device prefix for each log line. + * @prefix: additional prefix for each log line, may be NULL. + * @t: the timings data. + * @detailed: if true, give a detailed log. + */ +void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, + const struct v4l2_dv_timings *t, bool detailed); + /** v4l2_detect_cvt - detect if the given timings follow the CVT standard * @frame_height - the total height of the frame (including blanking) in lines. * @hfreq - the horizontal frequency in Hz. -- cgit v1.2.3-71-gd317 From ef1ed8f5d366a035e532456bd747d34e5cb01ee5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 15 Aug 2013 08:28:47 -0300 Subject: [media] v4l2-dv-timings: rename v4l_match_dv_timings to v4l2_match_dv_timings It's the only function in v4l2-dv-timings.c with the v4l prefix instead of v4l2. Make it consistent with the other functions. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 4 ++-- drivers/media/platform/s5p-tv/hdmi_drv.c | 2 +- drivers/media/usb/hdpvr/hdpvr-video.c | 2 +- drivers/media/v4l2-core/v4l2-dv-timings.c | 12 ++++++------ include/media/v4l2-dv-timings.h | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index ba8602c06d6b..a1a9d1e805f0 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -763,7 +763,7 @@ static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, int i; for (i = 0; predef_vid_timings[i].timings.bt.width; i++) { - if (!v4l_match_dv_timings(timings, &predef_vid_timings[i].timings, + if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings, DIGITAL_INPUT ? 250000 : 1000000)) continue; io_write(sd, 0x00, predef_vid_timings[i].vid_std); /* video std */ @@ -1183,7 +1183,7 @@ static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, int i; for (i = 0; adv7604_timings[i].bt.width; i++) { - if (v4l_match_dv_timings(timings, &adv7604_timings[i], + if (v4l2_match_dv_timings(timings, &adv7604_timings[i], DIGITAL_INPUT ? 250000 : 1000000)) { *timings = adv7604_timings[i]; break; diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 1b34c3629858..4ad9374913db 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -625,7 +625,7 @@ static int hdmi_s_dv_timings(struct v4l2_subdev *sd, int i; for (i = 0; i < ARRAY_SIZE(hdmi_timings); i++) - if (v4l_match_dv_timings(&hdmi_timings[i].dv_timings, + if (v4l2_match_dv_timings(&hdmi_timings[i].dv_timings, timings, 0)) break; if (i == ARRAY_SIZE(hdmi_timings)) { diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c index e68245a7f3a7..0500c4175d5f 100644 --- a/drivers/media/usb/hdpvr/hdpvr-video.c +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -642,7 +642,7 @@ static int vidioc_s_dv_timings(struct file *file, void *_fh, if (dev->status != STATUS_IDLE) return -EBUSY; for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++) - if (v4l_match_dv_timings(timings, hdpvr_dv_timings + i, 0)) + if (v4l2_match_dv_timings(timings, hdpvr_dv_timings + i, 0)) break; if (i == ARRAY_SIZE(hdpvr_dv_timings)) return -EINVAL; diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 917e58ce1f18..1a9d393307a1 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -181,7 +181,7 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, for (i = 0; i < ARRAY_SIZE(timings); i++) { if (v4l2_dv_valid_timings(timings + i, cap) && - v4l_match_dv_timings(t, timings + i, pclock_delta)) { + v4l2_match_dv_timings(t, timings + i, pclock_delta)) { *t = timings[i]; return true; } @@ -191,16 +191,16 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, EXPORT_SYMBOL_GPL(v4l2_find_dv_timings_cap); /** - * v4l_match_dv_timings - check if two timings match + * v4l2_match_dv_timings - check if two timings match * @t1 - compare this v4l2_dv_timings struct... * @t2 - with this struct. * @pclock_delta - the allowed pixelclock deviation. * * Compare t1 with t2 with a given margin of error for the pixelclock. */ -bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, - const struct v4l2_dv_timings *t2, - unsigned pclock_delta) +bool v4l2_match_dv_timings(const struct v4l2_dv_timings *t1, + const struct v4l2_dv_timings *t2, + unsigned pclock_delta) { if (t1->type != t2->type || t1->type != V4L2_DV_BT_656_1120) return false; @@ -221,7 +221,7 @@ bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1, return true; return false; } -EXPORT_SYMBOL_GPL(v4l_match_dv_timings); +EXPORT_SYMBOL_GPL(v4l2_match_dv_timings); void v4l2_print_dv_timings(const char *dev_prefix, const char *prefix, const struct v4l2_dv_timings *t, bool detailed) diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 696e5c2fd176..43f6b67af1cb 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -64,7 +64,7 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap, unsigned pclock_delta); -/** v4l_match_dv_timings() - do two timings match? +/** v4l2_match_dv_timings() - do two timings match? * @measured: the measured timings data. * @standard: the timings according to the standard. * @pclock_delta: maximum delta in Hz between standard->pixelclock and @@ -72,9 +72,9 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, * * Returns true if the two timings match, returns false otherwise. */ -bool v4l_match_dv_timings(const struct v4l2_dv_timings *measured, - const struct v4l2_dv_timings *standard, - unsigned pclock_delta); +bool v4l2_match_dv_timings(const struct v4l2_dv_timings *measured, + const struct v4l2_dv_timings *standard, + unsigned pclock_delta); /** v4l2_print_dv_timings() - log the contents of a dv_timings struct * @dev_prefix:device prefix for each log line. -- cgit v1.2.3-71-gd317 From d1c65ad6a44b0ff79d2f0bf726fa6fd9248991f4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 19 Aug 2013 10:19:54 -0300 Subject: [media] v4l2-dv-timings: export the timings list Some drivers need to be able to access the full list of timings. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dv-timings.c | 18 ++++++++++-------- include/media/v4l2-dv-timings.h | 4 ++++ 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index c2f5af7acbed..f515997a7341 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -26,7 +26,7 @@ #include #include -static const struct v4l2_dv_timings timings[] = { +const struct v4l2_dv_timings v4l2_dv_timings_presets[] = { V4L2_DV_BT_CEA_640X480P59_94, V4L2_DV_BT_CEA_720X480I59_94, V4L2_DV_BT_CEA_720X480P59_94, @@ -127,7 +127,9 @@ static const struct v4l2_dv_timings timings[] = { V4L2_DV_BT_DMT_2560X1600P75, V4L2_DV_BT_DMT_2560X1600P85, V4L2_DV_BT_DMT_2560X1600P120_RB, + { } }; +EXPORT_SYMBOL_GPL(v4l2_dv_timings_presets); bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *dvcap) @@ -159,10 +161,10 @@ int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, u32 i, idx; memset(t->reserved, 0, sizeof(t->reserved)); - for (i = idx = 0; i < ARRAY_SIZE(timings); i++) { - if (v4l2_dv_valid_timings(timings + i, cap) && + for (i = idx = 0; v4l2_dv_timings_presets[i].bt.width; i++) { + if (v4l2_dv_valid_timings(v4l2_dv_timings_presets + i, cap) && idx++ == t->index) { - t->timings = timings[i]; + t->timings = v4l2_dv_timings_presets[i]; return 0; } } @@ -179,10 +181,10 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, if (!v4l2_dv_valid_timings(t, cap)) return false; - for (i = 0; i < ARRAY_SIZE(timings); i++) { - if (v4l2_dv_valid_timings(timings + i, cap) && - v4l2_match_dv_timings(t, timings + i, pclock_delta)) { - *t = timings[i]; + for (i = 0; i < v4l2_dv_timings_presets[i].bt.width; i++) { + if (v4l2_dv_valid_timings(v4l2_dv_timings_presets + i, cap) && + v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i, pclock_delta)) { + *t = v4l2_dv_timings_presets[i]; return true; } } diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 43f6b67af1cb..0fe310b8bc80 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -23,6 +23,10 @@ #include +/** v4l2_dv_timings_presets: list of all dv_timings presets. + */ +extern const struct v4l2_dv_timings v4l2_dv_timings_presets[]; + /** v4l2_dv_valid_timings() - are these timings valid? * @t: the v4l2_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. -- cgit v1.2.3-71-gd317 From 70b654945bacd27622ef1c424f054ae04de597e0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 19 Aug 2013 10:23:33 -0300 Subject: [media] v4l2-dv-timings: rename v4l2_dv_valid_timings to v4l2_valid_dv_timings All other functions follow the v4l2__dv_timings pattern, do the same for this function. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ad9389b.c | 2 +- drivers/media/i2c/ths8200.c | 2 +- drivers/media/v4l2-core/v4l2-dv-timings.c | 10 +++++----- include/media/v4l2-dv-timings.h | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index bb74fb6b35c7..fc608516fc43 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -648,7 +648,7 @@ static int ad9389b_s_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s:\n", __func__); /* quick sanity check */ - if (!v4l2_dv_valid_timings(timings, &ad9389b_timings_cap)) + if (!v4l2_valid_dv_timings(timings, &ad9389b_timings_cap)) return -EINVAL; /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 580bd1b179fb..6abf0fb36079 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -378,7 +378,7 @@ static int ths8200_s_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s:\n", __func__); - if (!v4l2_dv_valid_timings(timings, &ths8200_timings_cap)) + if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap)) return -EINVAL; if (!v4l2_find_dv_timings_cap(timings, &ths8200_timings_cap, 10)) { diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index f515997a7341..a77f20145881 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -131,7 +131,7 @@ const struct v4l2_dv_timings v4l2_dv_timings_presets[] = { }; EXPORT_SYMBOL_GPL(v4l2_dv_timings_presets); -bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, +bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *dvcap) { const struct v4l2_bt_timings *bt = &t->bt; @@ -153,7 +153,7 @@ bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, return false; return true; } -EXPORT_SYMBOL_GPL(v4l2_dv_valid_timings); +EXPORT_SYMBOL_GPL(v4l2_valid_dv_timings); int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, const struct v4l2_dv_timings_cap *cap) @@ -162,7 +162,7 @@ int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, memset(t->reserved, 0, sizeof(t->reserved)); for (i = idx = 0; v4l2_dv_timings_presets[i].bt.width; i++) { - if (v4l2_dv_valid_timings(v4l2_dv_timings_presets + i, cap) && + if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap) && idx++ == t->index) { t->timings = v4l2_dv_timings_presets[i]; return 0; @@ -178,11 +178,11 @@ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, { int i; - if (!v4l2_dv_valid_timings(t, cap)) + if (!v4l2_valid_dv_timings(t, cap)) return false; for (i = 0; i < v4l2_dv_timings_presets[i].bt.width; i++) { - if (v4l2_dv_valid_timings(v4l2_dv_timings_presets + i, cap) && + if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap) && v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i, pclock_delta)) { *t = v4l2_dv_timings_presets[i]; return true; diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 0fe310b8bc80..bd59df8125c6 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -27,14 +27,14 @@ */ extern const struct v4l2_dv_timings v4l2_dv_timings_presets[]; -/** v4l2_dv_valid_timings() - are these timings valid? +/** v4l2_valid_dv_timings() - are these timings valid? * @t: the v4l2_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. * * Returns true if the given dv_timings struct is supported by the * hardware capabilities, returns false otherwise. */ -bool v4l2_dv_valid_timings(const struct v4l2_dv_timings *t, +bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap); /** v4l2_enum_dv_timings_cap() - Helper function to enumerate possible DV timings based on capabilities -- cgit v1.2.3-71-gd317 From b8f0fff4279a1b85fa4b6d7d8b538c254edcb4a1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 19 Aug 2013 11:21:50 -0300 Subject: [media] v4l2-dv-timings: add callback to handle exceptions In most cases the v4l2_bt_timings_cap struct has all the information necessary to determine valid timings, but occasionally there are exceptions. Add a callback function to be able to test for those exceptions. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/ad9389b.c | 7 ++++--- drivers/media/i2c/ths8200.c | 9 +++++--- drivers/media/v4l2-core/v4l2-dv-timings.c | 25 +++++++++++++++-------- include/media/v4l2-dv-timings.h | 34 +++++++++++++++++++++++++------ 4 files changed, 55 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index fc608516fc43..836978602973 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -648,12 +648,12 @@ static int ad9389b_s_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s:\n", __func__); /* quick sanity check */ - if (!v4l2_valid_dv_timings(timings, &ad9389b_timings_cap)) + if (!v4l2_valid_dv_timings(timings, &ad9389b_timings_cap, NULL, NULL)) return -EINVAL; /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings if the format is one of the CEA or DMT timings. */ - v4l2_find_dv_timings_cap(timings, &ad9389b_timings_cap, 0); + v4l2_find_dv_timings_cap(timings, &ad9389b_timings_cap, 0, NULL, NULL); timings->bt.flags &= ~V4L2_DV_FL_REDUCED_FPS; @@ -691,7 +691,8 @@ static int ad9389b_g_dv_timings(struct v4l2_subdev *sd, static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { - return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap); + return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap, + NULL, NULL); } static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd, diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 6abf0fb36079..a58a8f663ffb 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -378,10 +378,12 @@ static int ths8200_s_dv_timings(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "%s:\n", __func__); - if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap)) + if (!v4l2_valid_dv_timings(timings, &ths8200_timings_cap, + NULL, NULL)) return -EINVAL; - if (!v4l2_find_dv_timings_cap(timings, &ths8200_timings_cap, 10)) { + if (!v4l2_find_dv_timings_cap(timings, &ths8200_timings_cap, 10, + NULL, NULL)) { v4l2_dbg(1, debug, sd, "Unsupported format\n"); return -EINVAL; } @@ -411,7 +413,8 @@ static int ths8200_g_dv_timings(struct v4l2_subdev *sd, static int ths8200_enum_dv_timings(struct v4l2_subdev *sd, struct v4l2_enum_dv_timings *timings) { - return v4l2_enum_dv_timings_cap(timings, &ths8200_timings_cap); + return v4l2_enum_dv_timings_cap(timings, &ths8200_timings_cap, + NULL, NULL); } static int ths8200_dv_timings_cap(struct v4l2_subdev *sd, diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index a77f20145881..ee52b9f4a944 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -132,7 +132,9 @@ const struct v4l2_dv_timings v4l2_dv_timings_presets[] = { EXPORT_SYMBOL_GPL(v4l2_dv_timings_presets); bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, - const struct v4l2_dv_timings_cap *dvcap) + const struct v4l2_dv_timings_cap *dvcap, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle) { const struct v4l2_bt_timings *bt = &t->bt; const struct v4l2_bt_timings_cap *cap = &dvcap->bt; @@ -151,18 +153,21 @@ bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, (bt->interlaced && !(caps & V4L2_DV_BT_CAP_INTERLACED)) || (!bt->interlaced && !(caps & V4L2_DV_BT_CAP_PROGRESSIVE))) return false; - return true; + return fnc == NULL || fnc(t, fnc_handle); } EXPORT_SYMBOL_GPL(v4l2_valid_dv_timings); int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, - const struct v4l2_dv_timings_cap *cap) + const struct v4l2_dv_timings_cap *cap, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle) { u32 i, idx; memset(t->reserved, 0, sizeof(t->reserved)); for (i = idx = 0; v4l2_dv_timings_presets[i].bt.width; i++) { - if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap) && + if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap, + fnc, fnc_handle) && idx++ == t->index) { t->timings = v4l2_dv_timings_presets[i]; return 0; @@ -174,16 +179,20 @@ EXPORT_SYMBOL_GPL(v4l2_enum_dv_timings_cap); bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap, - unsigned pclock_delta) + unsigned pclock_delta, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle) { int i; - if (!v4l2_valid_dv_timings(t, cap)) + if (!v4l2_valid_dv_timings(t, cap, fnc, fnc_handle)) return false; for (i = 0; i < v4l2_dv_timings_presets[i].bt.width; i++) { - if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap) && - v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i, pclock_delta)) { + if (v4l2_valid_dv_timings(v4l2_dv_timings_presets + i, cap, + fnc, fnc_handle) && + v4l2_match_dv_timings(t, v4l2_dv_timings_presets + i, + pclock_delta)) { *t = v4l2_dv_timings_presets[i]; return true; } diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index bd59df8125c6..4becc6716393 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -27,46 +27,68 @@ */ extern const struct v4l2_dv_timings v4l2_dv_timings_presets[]; +/** v4l2_check_dv_timings_fnc - timings check callback + * @t: the v4l2_dv_timings struct. + * @handle: a handle from the driver. + * + * Returns true if the given timings are valid. + */ +typedef bool v4l2_check_dv_timings_fnc(const struct v4l2_dv_timings *t, void *handle); + /** v4l2_valid_dv_timings() - are these timings valid? * @t: the v4l2_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. + * @fnc: callback to check if this timing is OK. May be NULL. + * @fnc_handle: a handle that is passed on to @fnc. * * Returns true if the given dv_timings struct is supported by the - * hardware capabilities, returns false otherwise. + * hardware capabilities and the callback function (if non-NULL), returns + * false otherwise. */ bool v4l2_valid_dv_timings(const struct v4l2_dv_timings *t, - const struct v4l2_dv_timings_cap *cap); + const struct v4l2_dv_timings_cap *cap, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle); /** v4l2_enum_dv_timings_cap() - Helper function to enumerate possible DV timings based on capabilities * @t: the v4l2_enum_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. + * @fnc: callback to check if this timing is OK. May be NULL. + * @fnc_handle: a handle that is passed on to @fnc. * * This enumerates dv_timings using the full list of possible CEA-861 and DMT * timings, filtering out any timings that are not supported based on the - * hardware capabilities. + * hardware capabilities and the callback function (if non-NULL). * * If a valid timing for the given index is found, it will fill in @t and * return 0, otherwise it returns -EINVAL. */ int v4l2_enum_dv_timings_cap(struct v4l2_enum_dv_timings *t, - const struct v4l2_dv_timings_cap *cap); + const struct v4l2_dv_timings_cap *cap, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle); /** v4l2_find_dv_timings_cap() - Find the closest timings struct * @t: the v4l2_enum_dv_timings struct. * @cap: the v4l2_dv_timings_cap capabilities. * @pclock_delta: maximum delta between t->pixelclock and the timing struct * under consideration. + * @fnc: callback to check if a given timings struct is OK. May be NULL. + * @fnc_handle: a handle that is passed on to @fnc. * * This function tries to map the given timings to an entry in the * full list of possible CEA-861 and DMT timings, filtering out any timings - * that are not supported based on the hardware capabilities. + * that are not supported based on the hardware capabilities and the callback + * function (if non-NULL). * * On success it will fill in @t with the found timings and it returns true. * On failure it will return false. */ bool v4l2_find_dv_timings_cap(struct v4l2_dv_timings *t, const struct v4l2_dv_timings_cap *cap, - unsigned pclock_delta); + unsigned pclock_delta, + v4l2_check_dv_timings_fnc fnc, + void *fnc_handle); /** v4l2_match_dv_timings() - do two timings match? * @measured: the measured timings data. -- cgit v1.2.3-71-gd317 From 0f102b6ccec15c057d4d82f9731e6b780c9f8132 Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Mon, 3 Jun 2013 10:02:59 +0800 Subject: ARM: mmp: avoid to include head file in mach-mmp pxa910_set_wake() & mmp2_set_wake() are both declared in head files of arch/arm/mach-mmp/include/mach directory. If we include these head files in irq-mmp driver, it blocks the multiplatform build. So adjust the code. Signed-off-by: Haojian Zhuang --- arch/arm/mach-mmp/mmp2.c | 6 ++++++ arch/arm/mach-mmp/pxa910.c | 6 ++++++ drivers/irqchip/irq-mmp.c | 15 +-------------- include/linux/irqchip/mmp.h | 6 ++++++ 4 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 include/linux/irqchip/mmp.h (limited to 'include') diff --git a/arch/arm/mach-mmp/mmp2.c b/arch/arm/mach-mmp/mmp2.c index c7592f168bbd..a70b5530bd42 100644 --- a/arch/arm/mach-mmp/mmp2.c +++ b/arch/arm/mach-mmp/mmp2.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -26,6 +28,7 @@ #include #include #include +#include #include "common.h" @@ -94,6 +97,9 @@ void mmp2_clear_pmic_int(void) void __init mmp2_init_irq(void) { mmp2_init_icu(); +#ifdef CONFIG_PM + icu_irq_chip.irq_set_wake = mmp2_set_wake; +#endif } static int __init mmp2_init(void) diff --git a/arch/arm/mach-mmp/pxa910.c b/arch/arm/mach-mmp/pxa910.c index a586742fabf2..eb57ee196842 100644 --- a/arch/arm/mach-mmp/pxa910.c +++ b/arch/arm/mach-mmp/pxa910.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -23,6 +25,7 @@ #include #include #include +#include #include #include "common.h" @@ -80,6 +83,9 @@ static struct mfp_addr_map pxa910_mfp_addr_map[] __initdata = void __init pxa910_init_irq(void) { icu_init_irq(); +#ifdef CONFIG_PM + icu_irq_chip.irq_set_wake = pxa910_set_wake; +#endif } static int __init pxa910_init(void) diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c index 84d51ff836ee..1f8143278d4b 100644 --- a/drivers/irqchip/irq-mmp.c +++ b/drivers/irqchip/irq-mmp.c @@ -26,13 +26,6 @@ #include -#ifdef CONFIG_CPU_MMP2 -#include -#endif -#ifdef CONFIG_CPU_PXA910 -#include -#endif - #include "irqchip.h" #define MAX_ICU_NR 16 @@ -132,7 +125,7 @@ static void icu_unmask_irq(struct irq_data *d) } } -static struct irq_chip icu_irq_chip = { +struct irq_chip icu_irq_chip = { .name = "icu_irq", .irq_mask = icu_mask_irq, .irq_mask_ack = icu_mask_ack_irq, @@ -251,9 +244,6 @@ void __init icu_init_irq(void) } irq_set_default_host(icu_data[0].domain); set_handle_irq(mmp_handle_irq); -#ifdef CONFIG_CPU_PXA910 - icu_irq_chip.irq_set_wake = pxa910_set_wake; -#endif } /* MMP2 (ARMv7) */ @@ -358,9 +348,6 @@ void __init mmp2_init_icu(void) } irq_set_default_host(icu_data[0].domain); set_handle_irq(mmp2_handle_irq); -#ifdef CONFIG_CPU_MMP2 - icu_irq_chip.irq_set_wake = mmp2_set_wake; -#endif } #ifdef CONFIG_OF diff --git a/include/linux/irqchip/mmp.h b/include/linux/irqchip/mmp.h new file mode 100644 index 000000000000..c78a8921185d --- /dev/null +++ b/include/linux/irqchip/mmp.h @@ -0,0 +1,6 @@ +#ifndef __IRQCHIP_MMP_H +#define __IRQCHIP_MMP_H + +extern struct irq_chip icu_irq_chip; + +#endif /* __IRQCHIP_MMP_H */ -- cgit v1.2.3-71-gd317 From 7198e6b03155f6dadecadba004eb83b81a6ffe4c Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 19 Jul 2013 12:59:32 -0400 Subject: drm/msm: add a3xx gpu support Add initial support for a3xx 3d core. So far, with hardware that I've seen to date, we can have: + zero, one, or two z180 2d cores + a3xx or a2xx 3d core, which share a common CP (the firmware for the CP seems to implement some different PM4 packet types but the basics of cmdstream submission are the same) Which means that the eventual complete "class" hierarchy, once support for all past and present hw is in place, becomes: + msm_gpu + adreno_gpu + a3xx_gpu + a2xx_gpu + z180_gpu This commit splits out the parts that will eventually be common between a2xx/a3xx into adreno_gpu, and the parts that are even common to z180 into msm_gpu. Note that there is no cmdstream validation required. All memory access from the GPU is via IOMMU/MMU. So as long as you don't map silly things to the GPU, there isn't much damage that the GPU can do. Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/Makefile | 7 +- drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 501 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/msm/adreno/a3xx_gpu.h | 30 ++ drivers/gpu/drm/msm/adreno/adreno_gpu.c | 350 ++++++++++++++++++++++ drivers/gpu/drm/msm/adreno/adreno_gpu.h | 142 +++++++++ drivers/gpu/drm/msm/msm_drv.c | 246 +++++++++++++++- drivers/gpu/drm/msm/msm_drv.h | 44 ++- drivers/gpu/drm/msm/msm_gem.c | 84 +++++- drivers/gpu/drm/msm/msm_gem.h | 58 ++++ drivers/gpu/drm/msm/msm_gem_submit.c | 412 ++++++++++++++++++++++++++ drivers/gpu/drm/msm/msm_gpu.c | 411 ++++++++++++++++++++++++++ drivers/gpu/drm/msm/msm_gpu.h | 114 ++++++++ drivers/gpu/drm/msm/msm_ringbuffer.c | 61 ++++ drivers/gpu/drm/msm/msm_ringbuffer.h | 43 +++ include/uapi/drm/Kbuild | 1 + include/uapi/drm/msm_drm.h | 207 +++++++++++++ 16 files changed, 2695 insertions(+), 16 deletions(-) create mode 100644 drivers/gpu/drm/msm/adreno/a3xx_gpu.c create mode 100644 drivers/gpu/drm/msm/adreno/a3xx_gpu.h create mode 100644 drivers/gpu/drm/msm/adreno/adreno_gpu.c create mode 100644 drivers/gpu/drm/msm/adreno/adreno_gpu.h create mode 100644 drivers/gpu/drm/msm/msm_gem_submit.c create mode 100644 drivers/gpu/drm/msm/msm_gpu.c create mode 100644 drivers/gpu/drm/msm/msm_gpu.h create mode 100644 drivers/gpu/drm/msm/msm_ringbuffer.c create mode 100644 drivers/gpu/drm/msm/msm_ringbuffer.h create mode 100644 include/uapi/drm/msm_drm.h (limited to 'include') diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 4068122a9377..439dfb5b417b 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -4,6 +4,8 @@ ifeq (, $(findstring -W,$(EXTRA_CFLAGS))) endif msm-y := \ + adreno/adreno_gpu.o \ + adreno/a3xx_gpu.o \ hdmi/hdmi.o \ hdmi/hdmi_connector.o \ hdmi/hdmi_i2c.o \ @@ -18,7 +20,10 @@ msm-y := \ msm_connector.o \ msm_drv.o \ msm_fb.o \ - msm_gem.o + msm_gem.o \ + msm_gem_submit.o \ + msm_gpu.o \ + msm_ringbuffer.o msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c new file mode 100644 index 000000000000..13d61bbed302 --- /dev/null +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -0,0 +1,501 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "a3xx_gpu.h" + +#define A3XX_INT0_MASK \ + (A3XX_INT0_RBBM_AHB_ERROR | \ + A3XX_INT0_RBBM_ATB_BUS_OVERFLOW | \ + A3XX_INT0_CP_T0_PACKET_IN_IB | \ + A3XX_INT0_CP_OPCODE_ERROR | \ + A3XX_INT0_CP_RESERVED_BIT_ERROR | \ + A3XX_INT0_CP_HW_FAULT | \ + A3XX_INT0_CP_IB1_INT | \ + A3XX_INT0_CP_IB2_INT | \ + A3XX_INT0_CP_RB_INT | \ + A3XX_INT0_CP_REG_PROTECT_FAULT | \ + A3XX_INT0_CP_AHB_ERROR_HALT | \ + A3XX_INT0_UCHE_OOB_ACCESS) + +static struct platform_device *a3xx_pdev; + +static void a3xx_me_init(struct msm_gpu *gpu) +{ + struct msm_ringbuffer *ring = gpu->rb; + + OUT_PKT3(ring, CP_ME_INIT, 17); + OUT_RING(ring, 0x000003f7); + OUT_RING(ring, 0x00000000); + OUT_RING(ring, 0x00000000); + OUT_RING(ring, 0x00000000); + OUT_RING(ring, 0x00000080); + OUT_RING(ring, 0x00000100); + OUT_RING(ring, 0x00000180); + OUT_RING(ring, 0x00006600); + OUT_RING(ring, 0x00000150); + OUT_RING(ring, 0x0000014e); + OUT_RING(ring, 0x00000154); + OUT_RING(ring, 0x00000001); + OUT_RING(ring, 0x00000000); + OUT_RING(ring, 0x00000000); + OUT_RING(ring, 0x00000000); + OUT_RING(ring, 0x00000000); + OUT_RING(ring, 0x00000000); + + gpu->funcs->flush(gpu); + gpu->funcs->idle(gpu); +} + +static int a3xx_hw_init(struct msm_gpu *gpu) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + uint32_t *ptr, len; + int i, ret; + + DBG("%s", gpu->name); + + if (adreno_is_a305(adreno_gpu)) { + /* Set up 16 deep read/write request queues: */ + gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF0, 0x10101010); + gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF1, 0x10101010); + gpu_write(gpu, REG_A3XX_VBIF_OUT_RD_LIM_CONF0, 0x10101010); + gpu_write(gpu, REG_A3XX_VBIF_OUT_WR_LIM_CONF0, 0x10101010); + gpu_write(gpu, REG_A3XX_VBIF_DDR_OUT_MAX_BURST, 0x0000303); + gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF0, 0x10101010); + gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF1, 0x10101010); + /* Enable WR-REQ: */ + gpu_write(gpu, REG_A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x0000ff); + /* Set up round robin arbitration between both AXI ports: */ + gpu_write(gpu, REG_A3XX_VBIF_ARB_CTL, 0x00000030); + /* Set up AOOO: */ + gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000003c); + gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO, 0x003c003c); + + } else if (adreno_is_a320(adreno_gpu)) { + /* Set up 16 deep read/write request queues: */ + gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF0, 0x10101010); + gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF1, 0x10101010); + gpu_write(gpu, REG_A3XX_VBIF_OUT_RD_LIM_CONF0, 0x10101010); + gpu_write(gpu, REG_A3XX_VBIF_OUT_WR_LIM_CONF0, 0x10101010); + gpu_write(gpu, REG_A3XX_VBIF_DDR_OUT_MAX_BURST, 0x0000303); + gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF0, 0x10101010); + gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF1, 0x10101010); + /* Enable WR-REQ: */ + gpu_write(gpu, REG_A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x0000ff); + /* Set up round robin arbitration between both AXI ports: */ + gpu_write(gpu, REG_A3XX_VBIF_ARB_CTL, 0x00000030); + /* Set up AOOO: */ + gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000003c); + gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO, 0x003c003c); + /* Enable 1K sort: */ + gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT, 0x000000ff); + gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT_CONF, 0x000000a4); + + } else if (adreno_is_a330(adreno_gpu)) { + /* Set up 16 deep read/write request queues: */ + gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF0, 0x18181818); + gpu_write(gpu, REG_A3XX_VBIF_IN_RD_LIM_CONF1, 0x18181818); + gpu_write(gpu, REG_A3XX_VBIF_OUT_RD_LIM_CONF0, 0x18181818); + gpu_write(gpu, REG_A3XX_VBIF_OUT_WR_LIM_CONF0, 0x18181818); + gpu_write(gpu, REG_A3XX_VBIF_DDR_OUT_MAX_BURST, 0x0000303); + gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF0, 0x18181818); + gpu_write(gpu, REG_A3XX_VBIF_IN_WR_LIM_CONF1, 0x18181818); + /* Enable WR-REQ: */ + gpu_write(gpu, REG_A3XX_VBIF_GATE_OFF_WRREQ_EN, 0x00003f); + /* Set up round robin arbitration between both AXI ports: */ + gpu_write(gpu, REG_A3XX_VBIF_ARB_CTL, 0x00000030); + /* Set up VBIF_ROUND_ROBIN_QOS_ARB: */ + gpu_write(gpu, REG_A3XX_VBIF_ROUND_ROBIN_QOS_ARB, 0x0001); + /* Set up AOOO: */ + gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO_EN, 0x0000ffff); + gpu_write(gpu, REG_A3XX_VBIF_OUT_AXI_AOOO, 0xffffffff); + /* Enable 1K sort: */ + gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT, 0x0001ffff); + gpu_write(gpu, REG_A3XX_VBIF_ABIT_SORT_CONF, 0x000000a4); + /* Disable VBIF clock gating. This is to enable AXI running + * higher frequency than GPU: + */ + gpu_write(gpu, REG_A3XX_VBIF_CLKON, 0x00000001); + + } else { + BUG(); + } + + /* Make all blocks contribute to the GPU BUSY perf counter: */ + gpu_write(gpu, REG_A3XX_RBBM_GPU_BUSY_MASKED, 0xffffffff); + + /* Tune the hystersis counters for SP and CP idle detection: */ + gpu_write(gpu, REG_A3XX_RBBM_SP_HYST_CNT, 0x10); + gpu_write(gpu, REG_A3XX_RBBM_WAIT_IDLE_CLOCKS_CTL, 0x10); + + /* Enable the RBBM error reporting bits. This lets us get + * useful information on failure: + */ + gpu_write(gpu, REG_A3XX_RBBM_AHB_CTL0, 0x00000001); + + /* Enable AHB error reporting: */ + gpu_write(gpu, REG_A3XX_RBBM_AHB_CTL1, 0xa6ffffff); + + /* Turn on the power counters: */ + gpu_write(gpu, REG_A3XX_RBBM_RBBM_CTL, 0x00030000); + + /* Turn on hang detection - this spews a lot of useful information + * into the RBBM registers on a hang: + */ + gpu_write(gpu, REG_A3XX_RBBM_INTERFACE_HANG_INT_CTL, 0x00010fff); + + /* Enable 64-byte cacheline size. HW Default is 32-byte (0x000000E0): */ + gpu_write(gpu, REG_A3XX_UCHE_CACHE_MODE_CONTROL_REG, 0x00000001); + + /* Enable Clock gating: */ + gpu_write(gpu, REG_A3XX_RBBM_CLOCK_CTL, 0xbfffffff); + + /* Set the OCMEM base address for A330 */ +//TODO: +// if (adreno_is_a330(adreno_gpu)) { +// gpu_write(gpu, REG_A3XX_RB_GMEM_BASE_ADDR, +// (unsigned int)(a3xx_gpu->ocmem_base >> 14)); +// } + + /* Turn on performance counters: */ + gpu_write(gpu, REG_A3XX_RBBM_PERFCTR_CTL, 0x01); + + /* Set SP perfcounter 7 to count SP_FS_FULL_ALU_INSTRUCTIONS + * we will use this to augment our hang detection: + */ + gpu_write(gpu, REG_A3XX_SP_PERFCOUNTER7_SELECT, + SP_FS_FULL_ALU_INSTRUCTIONS); + + gpu_write(gpu, REG_A3XX_RBBM_INT_0_MASK, A3XX_INT0_MASK); + + ret = adreno_hw_init(gpu); + if (ret) + return ret; + + /* setup access protection: */ + gpu_write(gpu, REG_A3XX_CP_PROTECT_CTRL, 0x00000007); + + /* RBBM registers */ + gpu_write(gpu, REG_A3XX_CP_PROTECT(0), 0x63000040); + gpu_write(gpu, REG_A3XX_CP_PROTECT(1), 0x62000080); + gpu_write(gpu, REG_A3XX_CP_PROTECT(2), 0x600000cc); + gpu_write(gpu, REG_A3XX_CP_PROTECT(3), 0x60000108); + gpu_write(gpu, REG_A3XX_CP_PROTECT(4), 0x64000140); + gpu_write(gpu, REG_A3XX_CP_PROTECT(5), 0x66000400); + + /* CP registers */ + gpu_write(gpu, REG_A3XX_CP_PROTECT(6), 0x65000700); + gpu_write(gpu, REG_A3XX_CP_PROTECT(7), 0x610007d8); + gpu_write(gpu, REG_A3XX_CP_PROTECT(8), 0x620007e0); + gpu_write(gpu, REG_A3XX_CP_PROTECT(9), 0x61001178); + gpu_write(gpu, REG_A3XX_CP_PROTECT(10), 0x64001180); + + /* RB registers */ + gpu_write(gpu, REG_A3XX_CP_PROTECT(11), 0x60003300); + + /* VBIF registers */ + gpu_write(gpu, REG_A3XX_CP_PROTECT(12), 0x6b00c000); + + /* NOTE: PM4/micro-engine firmware registers look to be the same + * for a2xx and a3xx.. we could possibly push that part down to + * adreno_gpu base class. Or push both PM4 and PFP but + * parameterize the pfp ucode addr/data registers.. + */ + + /* Load PM4: */ + ptr = (uint32_t *)(adreno_gpu->pm4->data); + len = adreno_gpu->pm4->size / 4; + DBG("loading PM4 ucode version: %u", ptr[0]); + + gpu_write(gpu, REG_AXXX_CP_DEBUG, + AXXX_CP_DEBUG_DYNAMIC_CLK_DISABLE | + AXXX_CP_DEBUG_MIU_128BIT_WRITE_ENABLE); + gpu_write(gpu, REG_AXXX_CP_ME_RAM_WADDR, 0); + for (i = 1; i < len; i++) + gpu_write(gpu, REG_AXXX_CP_ME_RAM_DATA, ptr[i]); + + /* Load PFP: */ + ptr = (uint32_t *)(adreno_gpu->pfp->data); + len = adreno_gpu->pfp->size / 4; + DBG("loading PFP ucode version: %u", ptr[0]); + + gpu_write(gpu, REG_A3XX_CP_PFP_UCODE_ADDR, 0); + for (i = 1; i < len; i++) + gpu_write(gpu, REG_A3XX_CP_PFP_UCODE_DATA, ptr[i]); + + /* CP ROQ queue sizes (bytes) - RB:16, ST:16, IB1:32, IB2:64 */ + if (adreno_is_a305(adreno_gpu) || adreno_is_a320(adreno_gpu)) + gpu_write(gpu, REG_AXXX_CP_QUEUE_THRESHOLDS, + AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB1_START(2) | + AXXX_CP_QUEUE_THRESHOLDS_CSQ_IB2_START(6) | + AXXX_CP_QUEUE_THRESHOLDS_CSQ_ST_START(14)); + + + /* clear ME_HALT to start micro engine */ + gpu_write(gpu, REG_AXXX_CP_ME_CNTL, 0); + + a3xx_me_init(gpu); + + return 0; +} + +static void a3xx_destroy(struct msm_gpu *gpu) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a3xx_gpu *a3xx_gpu = to_a3xx_gpu(adreno_gpu); + + DBG("%s", gpu->name); + + adreno_gpu_cleanup(adreno_gpu); + put_device(&a3xx_gpu->pdev->dev); + kfree(a3xx_gpu); +} + +static void a3xx_idle(struct msm_gpu *gpu) +{ + unsigned long t; + + /* wait for ringbuffer to drain: */ + adreno_idle(gpu); + + t = jiffies + ADRENO_IDLE_TIMEOUT; + + /* then wait for GPU to finish: */ + do { + uint32_t rbbm_status = gpu_read(gpu, REG_A3XX_RBBM_STATUS); + if (!(rbbm_status & A3XX_RBBM_STATUS_GPU_BUSY)) + return; + } while(time_before(jiffies, t)); + + DRM_ERROR("timeout waiting for %s to idle!\n", gpu->name); + + /* TODO maybe we need to reset GPU here to recover from hang? */ +} + +static irqreturn_t a3xx_irq(struct msm_gpu *gpu) +{ + uint32_t status; + + status = gpu_read(gpu, REG_A3XX_RBBM_INT_0_STATUS); + DBG("%s: %08x", gpu->name, status); + + // TODO + + gpu_write(gpu, REG_A3XX_RBBM_INT_CLEAR_CMD, status); + + msm_gpu_retire(gpu); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_DEBUG_FS +static const unsigned int a3xx_registers[] = { + 0x0000, 0x0002, 0x0010, 0x0012, 0x0018, 0x0018, 0x0020, 0x0027, + 0x0029, 0x002b, 0x002e, 0x0033, 0x0040, 0x0042, 0x0050, 0x005c, + 0x0060, 0x006c, 0x0080, 0x0082, 0x0084, 0x0088, 0x0090, 0x00e5, + 0x00ea, 0x00ed, 0x0100, 0x0100, 0x0110, 0x0123, 0x01c0, 0x01c1, + 0x01c3, 0x01c5, 0x01c7, 0x01c7, 0x01d5, 0x01d9, 0x01dc, 0x01dd, + 0x01ea, 0x01ea, 0x01ee, 0x01f1, 0x01f5, 0x01f5, 0x01fc, 0x01ff, + 0x0440, 0x0440, 0x0443, 0x0443, 0x0445, 0x0445, 0x044d, 0x044f, + 0x0452, 0x0452, 0x0454, 0x046f, 0x047c, 0x047c, 0x047f, 0x047f, + 0x0578, 0x057f, 0x0600, 0x0602, 0x0605, 0x0607, 0x060a, 0x060e, + 0x0612, 0x0614, 0x0c01, 0x0c02, 0x0c06, 0x0c1d, 0x0c3d, 0x0c3f, + 0x0c48, 0x0c4b, 0x0c80, 0x0c80, 0x0c88, 0x0c8b, 0x0ca0, 0x0cb7, + 0x0cc0, 0x0cc1, 0x0cc6, 0x0cc7, 0x0ce4, 0x0ce5, 0x0e00, 0x0e05, + 0x0e0c, 0x0e0c, 0x0e22, 0x0e23, 0x0e41, 0x0e45, 0x0e64, 0x0e65, + 0x0e80, 0x0e82, 0x0e84, 0x0e89, 0x0ea0, 0x0ea1, 0x0ea4, 0x0ea7, + 0x0ec4, 0x0ecb, 0x0ee0, 0x0ee0, 0x0f00, 0x0f01, 0x0f03, 0x0f09, + 0x2040, 0x2040, 0x2044, 0x2044, 0x2048, 0x204d, 0x2068, 0x2069, + 0x206c, 0x206d, 0x2070, 0x2070, 0x2072, 0x2072, 0x2074, 0x2075, + 0x2079, 0x207a, 0x20c0, 0x20d3, 0x20e4, 0x20ef, 0x2100, 0x2109, + 0x210c, 0x210c, 0x210e, 0x210e, 0x2110, 0x2111, 0x2114, 0x2115, + 0x21e4, 0x21e4, 0x21ea, 0x21ea, 0x21ec, 0x21ed, 0x21f0, 0x21f0, + 0x2200, 0x2212, 0x2214, 0x2217, 0x221a, 0x221a, 0x2240, 0x227e, + 0x2280, 0x228b, 0x22c0, 0x22c0, 0x22c4, 0x22ce, 0x22d0, 0x22d8, + 0x22df, 0x22e6, 0x22e8, 0x22e9, 0x22ec, 0x22ec, 0x22f0, 0x22f7, + 0x22ff, 0x22ff, 0x2340, 0x2343, 0x2348, 0x2349, 0x2350, 0x2356, + 0x2360, 0x2360, 0x2440, 0x2440, 0x2444, 0x2444, 0x2448, 0x244d, + 0x2468, 0x2469, 0x246c, 0x246d, 0x2470, 0x2470, 0x2472, 0x2472, + 0x2474, 0x2475, 0x2479, 0x247a, 0x24c0, 0x24d3, 0x24e4, 0x24ef, + 0x2500, 0x2509, 0x250c, 0x250c, 0x250e, 0x250e, 0x2510, 0x2511, + 0x2514, 0x2515, 0x25e4, 0x25e4, 0x25ea, 0x25ea, 0x25ec, 0x25ed, + 0x25f0, 0x25f0, 0x2600, 0x2612, 0x2614, 0x2617, 0x261a, 0x261a, + 0x2640, 0x267e, 0x2680, 0x268b, 0x26c0, 0x26c0, 0x26c4, 0x26ce, + 0x26d0, 0x26d8, 0x26df, 0x26e6, 0x26e8, 0x26e9, 0x26ec, 0x26ec, + 0x26f0, 0x26f7, 0x26ff, 0x26ff, 0x2740, 0x2743, 0x2748, 0x2749, + 0x2750, 0x2756, 0x2760, 0x2760, 0x300c, 0x300e, 0x301c, 0x301d, + 0x302a, 0x302a, 0x302c, 0x302d, 0x3030, 0x3031, 0x3034, 0x3036, + 0x303c, 0x303c, 0x305e, 0x305f, +}; + +static void a3xx_show(struct msm_gpu *gpu, struct seq_file *m) +{ + int i; + + adreno_show(gpu, m); + seq_printf(m, "status: %08x\n", + gpu_read(gpu, REG_A3XX_RBBM_STATUS)); + + /* dump these out in a form that can be parsed by demsm: */ + seq_printf(m, "IO:region %s 00000000 00020000\n", gpu->name); + for (i = 0; i < ARRAY_SIZE(a3xx_registers); i += 2) { + uint32_t start = a3xx_registers[i]; + uint32_t end = a3xx_registers[i+1]; + uint32_t addr; + + for (addr = start; addr <= end; addr++) { + uint32_t val = gpu_read(gpu, addr); + seq_printf(m, "IO:R %08x %08x\n", addr<<2, val); + } + } +} +#endif + +static const struct adreno_gpu_funcs funcs = { + .base = { + .get_param = adreno_get_param, + .hw_init = a3xx_hw_init, + .pm_suspend = msm_gpu_pm_suspend, + .pm_resume = msm_gpu_pm_resume, + .last_fence = adreno_last_fence, + .submit = adreno_submit, + .flush = adreno_flush, + .idle = a3xx_idle, + .irq = a3xx_irq, + .destroy = a3xx_destroy, +#ifdef CONFIG_DEBUG_FS + .show = a3xx_show, +#endif + }, +}; + +struct msm_gpu *a3xx_gpu_init(struct drm_device *dev) +{ + struct a3xx_gpu *a3xx_gpu = NULL; + struct msm_gpu *gpu; + struct platform_device *pdev = a3xx_pdev; + struct adreno_platform_config *config; + int ret; + + if (!pdev) { + dev_err(dev->dev, "no a3xx device\n"); + ret = -ENXIO; + goto fail; + } + + config = pdev->dev.platform_data; + + a3xx_gpu = kzalloc(sizeof(*a3xx_gpu), GFP_KERNEL); + if (!a3xx_gpu) { + ret = -ENOMEM; + goto fail; + } + + gpu = &a3xx_gpu->base.base; + + get_device(&pdev->dev); + a3xx_gpu->pdev = pdev; + + gpu->fast_rate = config->fast_rate; + gpu->slow_rate = config->slow_rate; + gpu->bus_freq = config->bus_freq; + + DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u", + gpu->fast_rate, gpu->slow_rate, gpu->bus_freq); + + ret = adreno_gpu_init(dev, pdev, &a3xx_gpu->base, + &funcs, config->rev); + if (ret) + goto fail; + + return &a3xx_gpu->base.base; + +fail: + if (a3xx_gpu) + a3xx_destroy(&a3xx_gpu->base.base); + + return ERR_PTR(ret); +} + +/* + * The a3xx device: + */ + +static int a3xx_probe(struct platform_device *pdev) +{ + static struct adreno_platform_config config = {}; +#ifdef CONFIG_OF + /* TODO */ +#else + uint32_t version = socinfo_get_version(); + if (cpu_is_apq8064ab()) { + config.fast_rate = 450000000; + config.slow_rate = 27000000; + config.bus_freq = 4; + config.rev = ADRENO_REV(3, 2, 1, 0); + } else if (cpu_is_apq8064() || cpu_is_msm8960ab()) { + config.fast_rate = 400000000; + config.slow_rate = 27000000; + config.bus_freq = 4; + + if (SOCINFO_VERSION_MAJOR(version) == 2) + config.rev = ADRENO_REV(3, 2, 0, 2); + else if ((SOCINFO_VERSION_MAJOR(version) == 1) && + (SOCINFO_VERSION_MINOR(version) == 1)) + config.rev = ADRENO_REV(3, 2, 0, 1); + else + config.rev = ADRENO_REV(3, 2, 0, 0); + + } else if (cpu_is_msm8930()) { + config.fast_rate = 400000000; + config.slow_rate = 27000000; + config.bus_freq = 3; + + if ((SOCINFO_VERSION_MAJOR(version) == 1) && + (SOCINFO_VERSION_MINOR(version) == 2)) + config.rev = ADRENO_REV(3, 0, 5, 2); + else + config.rev = ADRENO_REV(3, 0, 5, 0); + + } +#endif + pdev->dev.platform_data = &config; + a3xx_pdev = pdev; + return 0; +} + +static int a3xx_remove(struct platform_device *pdev) +{ + a3xx_pdev = NULL; + return 0; +} + +static struct platform_driver a3xx_driver = { + .probe = a3xx_probe, + .remove = a3xx_remove, + .driver.name = "kgsl-3d0", +}; + +void __init a3xx_register(void) +{ + platform_driver_register(&a3xx_driver); +} + +void __exit a3xx_unregister(void) +{ + platform_driver_unregister(&a3xx_driver); +} diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.h b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h new file mode 100644 index 000000000000..32c398c2d00a --- /dev/null +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef __A3XX_GPU_H__ +#define __A3XX_GPU_H__ + +#include "adreno_gpu.h" +#include "a3xx.xml.h" + +struct a3xx_gpu { + struct adreno_gpu base; + struct platform_device *pdev; +}; +#define to_a3xx_gpu(x) container_of(x, struct a3xx_gpu, base) + +#endif /* __A3XX_GPU_H__ */ diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c new file mode 100644 index 000000000000..282163ee3fa5 --- /dev/null +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "adreno_gpu.h" +#include "msm_gem.h" + +struct adreno_info { + struct adreno_rev rev; + uint32_t revn; + const char *name; + const char *pm4fw, *pfpfw; + uint32_t gmem; +}; + +#define ANY_ID 0xff + +static const struct adreno_info gpulist[] = { + { + .rev = ADRENO_REV(3, 0, 5, ANY_ID), + .revn = 305, + .name = "A305", + .pm4fw = "a300_pm4.fw", + .pfpfw = "a300_pfp.fw", + .gmem = SZ_256K, + }, { + .rev = ADRENO_REV(3, 2, ANY_ID, ANY_ID), + .revn = 320, + .name = "A320", + .pm4fw = "a300_pm4.fw", + .pfpfw = "a300_pfp.fw", + .gmem = SZ_512K, + }, { + .rev = ADRENO_REV(3, 3, 0, 0), + .revn = 330, + .name = "A330", + .pm4fw = "a330_pm4.fw", + .pfpfw = "a330_pfp.fw", + .gmem = SZ_1M, + }, +}; + +#define RB_SIZE SZ_32K +#define RB_BLKSIZE 16 + +int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + + switch (param) { + case MSM_PARAM_GPU_ID: + *value = adreno_gpu->info->revn; + return 0; + case MSM_PARAM_GMEM_SIZE: + *value = adreno_gpu->info->gmem; + return 0; + default: + DBG("%s: invalid param: %u", gpu->name, param); + return -EINVAL; + } +} + +#define rbmemptr(adreno_gpu, member) \ + ((adreno_gpu)->memptrs_iova + offsetof(struct adreno_rbmemptrs, member)) + +int adreno_hw_init(struct msm_gpu *gpu) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + + DBG("%s", gpu->name); + + /* Setup REG_CP_RB_CNTL: */ + gpu_write(gpu, REG_AXXX_CP_RB_CNTL, + /* size is log2(quad-words): */ + AXXX_CP_RB_CNTL_BUFSZ(ilog2(gpu->rb->size / 8)) | + AXXX_CP_RB_CNTL_BLKSZ(RB_BLKSIZE)); + + /* Setup ringbuffer address: */ + gpu_write(gpu, REG_AXXX_CP_RB_BASE, gpu->rb_iova); + gpu_write(gpu, REG_AXXX_CP_RB_RPTR_ADDR, rbmemptr(adreno_gpu, rptr)); + + /* Setup scratch/timestamp: */ + gpu_write(gpu, REG_AXXX_SCRATCH_ADDR, rbmemptr(adreno_gpu, fence)); + + gpu_write(gpu, REG_AXXX_SCRATCH_UMSK, 0x1); + + return 0; +} + +static uint32_t get_wptr(struct msm_ringbuffer *ring) +{ + return ring->cur - ring->start; +} + +uint32_t adreno_last_fence(struct msm_gpu *gpu) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + return adreno_gpu->memptrs->fence; +} + +int adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, + struct msm_file_private *ctx) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct msm_drm_private *priv = gpu->dev->dev_private; + struct msm_ringbuffer *ring = gpu->rb; + unsigned i, ibs = 0; + + adreno_gpu->last_fence = submit->fence; + + for (i = 0; i < submit->nr_cmds; i++) { + switch (submit->cmd[i].type) { + case MSM_SUBMIT_CMD_IB_TARGET_BUF: + /* ignore IB-targets */ + break; + case MSM_SUBMIT_CMD_CTX_RESTORE_BUF: + /* ignore if there has not been a ctx switch: */ + if (priv->lastctx == ctx) + break; + case MSM_SUBMIT_CMD_BUF: + OUT_PKT3(ring, CP_INDIRECT_BUFFER_PFD, 2); + OUT_RING(ring, submit->cmd[i].iova); + OUT_RING(ring, submit->cmd[i].size); + ibs++; + break; + } + } + + /* on a320, at least, we seem to need to pad things out to an + * even number of qwords to avoid issue w/ CP hanging on wrap- + * around: + */ + if (ibs % 2) + OUT_PKT2(ring); + + OUT_PKT0(ring, REG_AXXX_CP_SCRATCH_REG2, 1); + OUT_RING(ring, submit->fence); + + if (adreno_is_a3xx(adreno_gpu)) { + /* Flush HLSQ lazy updates to make sure there is nothing + * pending for indirect loads after the timestamp has + * passed: + */ + OUT_PKT3(ring, CP_EVENT_WRITE, 1); + OUT_RING(ring, HLSQ_FLUSH); + + OUT_PKT3(ring, CP_WAIT_FOR_IDLE, 1); + OUT_RING(ring, 0x00000000); + } + + OUT_PKT3(ring, CP_EVENT_WRITE, 3); + OUT_RING(ring, CACHE_FLUSH_TS); + OUT_RING(ring, rbmemptr(adreno_gpu, fence)); + OUT_RING(ring, submit->fence); + + /* we could maybe be clever and only CP_COND_EXEC the interrupt: */ + OUT_PKT3(ring, CP_INTERRUPT, 1); + OUT_RING(ring, 0x80000000); + +#if 0 + if (adreno_is_a3xx(adreno_gpu)) { + /* Dummy set-constant to trigger context rollover */ + OUT_PKT3(ring, CP_SET_CONSTANT, 2); + OUT_RING(ring, CP_REG(REG_A3XX_HLSQ_CL_KERNEL_GROUP_X_REG)); + OUT_RING(ring, 0x00000000); + } +#endif + + gpu->funcs->flush(gpu); + + return 0; +} + +void adreno_flush(struct msm_gpu *gpu) +{ + uint32_t wptr = get_wptr(gpu->rb); + + /* ensure writes to ringbuffer have hit system memory: */ + mb(); + + gpu_write(gpu, REG_AXXX_CP_RB_WPTR, wptr); +} + +void adreno_idle(struct msm_gpu *gpu) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + uint32_t rptr, wptr = get_wptr(gpu->rb); + unsigned long t; + + t = jiffies + ADRENO_IDLE_TIMEOUT; + + /* then wait for CP to drain ringbuffer: */ + do { + rptr = adreno_gpu->memptrs->rptr; + if (rptr == wptr) + return; + } while(time_before(jiffies, t)); + + DRM_ERROR("timeout waiting for %s to drain ringbuffer!\n", gpu->name); + + /* TODO maybe we need to reset GPU here to recover from hang? */ +} + +#ifdef CONFIG_DEBUG_FS +void adreno_show(struct msm_gpu *gpu, struct seq_file *m) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + + seq_printf(m, "revision: %d (%d.%d.%d.%d)\n", + adreno_gpu->info->revn, adreno_gpu->rev.core, + adreno_gpu->rev.major, adreno_gpu->rev.minor, + adreno_gpu->rev.patchid); + + seq_printf(m, "fence: %d/%d\n", adreno_gpu->memptrs->fence, + adreno_gpu->last_fence); + seq_printf(m, "rptr: %d\n", adreno_gpu->memptrs->rptr); + seq_printf(m, "wptr: %d\n", adreno_gpu->memptrs->wptr); + seq_printf(m, "rb wptr: %d\n", get_wptr(gpu->rb)); +} +#endif + +void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + uint32_t freedwords; + do { + uint32_t size = gpu->rb->size / 4; + uint32_t wptr = get_wptr(gpu->rb); + uint32_t rptr = adreno_gpu->memptrs->rptr; + freedwords = (rptr + (size - 1) - wptr) % size; + } while(freedwords < ndwords); +} + +static const char *iommu_ports[] = { + "gfx3d_user", "gfx3d_priv", + "gfx3d1_user", "gfx3d1_priv", +}; + +static inline bool _rev_match(uint8_t entry, uint8_t id) +{ + return (entry == ANY_ID) || (entry == id); +} + +int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, + struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs, + struct adreno_rev rev) +{ + int i, ret; + + /* identify gpu: */ + for (i = 0; i < ARRAY_SIZE(gpulist); i++) { + const struct adreno_info *info = &gpulist[i]; + if (_rev_match(info->rev.core, rev.core) && + _rev_match(info->rev.major, rev.major) && + _rev_match(info->rev.minor, rev.minor) && + _rev_match(info->rev.patchid, rev.patchid)) { + gpu->info = info; + gpu->revn = info->revn; + break; + } + } + + if (i == ARRAY_SIZE(gpulist)) { + dev_err(drm->dev, "Unknown GPU revision: %u.%u.%u.%u\n", + rev.core, rev.major, rev.minor, rev.patchid); + return -ENXIO; + } + + DBG("Found GPU: %s (%u.%u.%u.%u)", gpu->info->name, + rev.core, rev.major, rev.minor, rev.patchid); + + gpu->funcs = funcs; + gpu->rev = rev; + + ret = request_firmware(&gpu->pm4, gpu->info->pm4fw, drm->dev); + if (ret) { + dev_err(drm->dev, "failed to load %s PM4 firmware: %d\n", + gpu->info->pm4fw, ret); + return ret; + } + + ret = request_firmware(&gpu->pfp, gpu->info->pfpfw, drm->dev); + if (ret) { + dev_err(drm->dev, "failed to load %s PFP firmware: %d\n", + gpu->info->pfpfw, ret); + return ret; + } + + ret = msm_gpu_init(drm, pdev, &gpu->base, &funcs->base, + gpu->info->name, "kgsl_3d0_reg_memory", "kgsl_3d0_irq", + RB_SIZE); + if (ret) + return ret; + + ret = msm_iommu_attach(drm, gpu->base.iommu, + iommu_ports, ARRAY_SIZE(iommu_ports)); + if (ret) + return ret; + + gpu->memptrs_bo = msm_gem_new(drm, sizeof(*gpu->memptrs), + MSM_BO_UNCACHED); + if (IS_ERR(gpu->memptrs_bo)) { + ret = PTR_ERR(gpu->memptrs_bo); + gpu->memptrs_bo = NULL; + dev_err(drm->dev, "could not allocate memptrs: %d\n", ret); + return ret; + } + + gpu->memptrs = msm_gem_vaddr_locked(gpu->memptrs_bo); + if (!gpu->memptrs) { + dev_err(drm->dev, "could not vmap memptrs\n"); + return -ENOMEM; + } + + ret = msm_gem_get_iova_locked(gpu->memptrs_bo, gpu->base.id, + &gpu->memptrs_iova); + if (ret) { + dev_err(drm->dev, "could not map memptrs: %d\n", ret); + return ret; + } + + return 0; +} + +void adreno_gpu_cleanup(struct adreno_gpu *gpu) +{ + if (gpu->memptrs_bo) { + if (gpu->memptrs_iova) + msm_gem_put_iova(gpu->memptrs_bo, gpu->base.id); + drm_gem_object_unreference(gpu->memptrs_bo); + } + if (gpu->pm4) + release_firmware(gpu->pm4); + if (gpu->pfp) + release_firmware(gpu->pfp); + msm_gpu_cleanup(&gpu->base); +} diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h new file mode 100644 index 000000000000..6b49c4f27fec --- /dev/null +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef __ADRENO_GPU_H__ +#define __ADRENO_GPU_H__ + +#include + +#include "msm_gpu.h" + +#include "adreno_common.xml.h" +#include "adreno_pm4.xml.h" + +struct adreno_rev { + uint8_t core; + uint8_t major; + uint8_t minor; + uint8_t patchid; +}; + +#define ADRENO_REV(core, major, minor, patchid) \ + ((struct adreno_rev){ core, major, minor, patchid }) + +struct adreno_gpu_funcs { + struct msm_gpu_funcs base; +}; + +struct adreno_info; + +struct adreno_rbmemptrs { + volatile uint32_t rptr; + volatile uint32_t wptr; + volatile uint32_t fence; +}; + +struct adreno_gpu { + struct msm_gpu base; + struct adreno_rev rev; + const struct adreno_info *info; + uint32_t revn; /* numeric revision name */ + const struct adreno_gpu_funcs *funcs; + + uint32_t last_fence; + + /* firmware: */ + const struct firmware *pm4, *pfp; + + /* ringbuffer rptr/wptr: */ + // TODO should this be in msm_ringbuffer? I think it would be + // different for z180.. + struct adreno_rbmemptrs *memptrs; + struct drm_gem_object *memptrs_bo; + uint32_t memptrs_iova; +}; +#define to_adreno_gpu(x) container_of(x, struct adreno_gpu, base) + +/* platform config data (ie. from DT, or pdata) */ +struct adreno_platform_config { + struct adreno_rev rev; + uint32_t fast_rate, slow_rate, bus_freq; +}; + +#define ADRENO_IDLE_TIMEOUT (20 * 1000) + +static inline bool adreno_is_a3xx(struct adreno_gpu *gpu) +{ + return (gpu->revn >= 300) && (gpu->revn < 400); +} + +static inline bool adreno_is_a305(struct adreno_gpu *gpu) +{ + return gpu->revn == 305; +} + +static inline bool adreno_is_a320(struct adreno_gpu *gpu) +{ + return gpu->revn == 320; +} + +static inline bool adreno_is_a330(struct adreno_gpu *gpu) +{ + return gpu->revn == 330; +} + +int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value); +int adreno_hw_init(struct msm_gpu *gpu); +uint32_t adreno_last_fence(struct msm_gpu *gpu); +int adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, + struct msm_file_private *ctx); +void adreno_flush(struct msm_gpu *gpu); +void adreno_idle(struct msm_gpu *gpu); +#ifdef CONFIG_DEBUG_FS +void adreno_show(struct msm_gpu *gpu, struct seq_file *m); +#endif +void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords); + +int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, + struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs, + struct adreno_rev rev); +void adreno_gpu_cleanup(struct adreno_gpu *gpu); + + +/* ringbuffer helpers (the parts that are adreno specific) */ + +static inline void +OUT_PKT0(struct msm_ringbuffer *ring, uint16_t regindx, uint16_t cnt) +{ + adreno_wait_ring(ring->gpu, cnt+1); + OUT_RING(ring, CP_TYPE0_PKT | ((cnt-1) << 16) | (regindx & 0x7FFF)); +} + +/* no-op packet: */ +static inline void +OUT_PKT2(struct msm_ringbuffer *ring) +{ + adreno_wait_ring(ring->gpu, 1); + OUT_RING(ring, CP_TYPE2_PKT); +} + +static inline void +OUT_PKT3(struct msm_ringbuffer *ring, uint8_t opcode, uint16_t cnt) +{ + adreno_wait_ring(ring->gpu, cnt+1); + OUT_RING(ring, CP_TYPE3_PKT | ((cnt-1) << 16) | ((opcode & 0xFF) << 8)); +} + + +#endif /* __ADRENO_GPU_H__ */ diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index b5ae0dbe1eb8..864c9773636b 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -16,6 +16,7 @@ */ #include "msm_drv.h" +#include "msm_gpu.h" #include @@ -135,6 +136,7 @@ static int msm_unload(struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private; struct msm_kms *kms = priv->kms; + struct msm_gpu *gpu = priv->gpu; drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); @@ -152,6 +154,12 @@ static int msm_unload(struct drm_device *dev) kms->funcs->destroy(kms); } + if (gpu) { + mutex_lock(&dev->struct_mutex); + gpu->funcs->pm_suspend(gpu); + gpu->funcs->destroy(gpu); + mutex_unlock(&dev->struct_mutex); + } dev->dev_private = NULL; @@ -176,6 +184,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags) dev->dev_private = priv; priv->wq = alloc_ordered_workqueue("msm", 0); + init_waitqueue_head(&priv->fence_event); INIT_LIST_HEAD(&priv->inactive_list); @@ -240,12 +249,70 @@ fail: return ret; } +static void load_gpu(struct drm_device *dev) +{ + struct msm_drm_private *priv = dev->dev_private; + struct msm_gpu *gpu; + + if (priv->gpu) + return; + + mutex_lock(&dev->struct_mutex); + gpu = a3xx_gpu_init(dev); + if (IS_ERR(gpu)) { + dev_warn(dev->dev, "failed to load a3xx gpu\n"); + gpu = NULL; + /* not fatal */ + } + mutex_unlock(&dev->struct_mutex); + + if (gpu) { + int ret; + gpu->funcs->pm_resume(gpu); + ret = gpu->funcs->hw_init(gpu); + if (ret) { + dev_err(dev->dev, "gpu hw init failed: %d\n", ret); + gpu->funcs->destroy(gpu); + gpu = NULL; + } + } + + priv->gpu = gpu; +} + +static int msm_open(struct drm_device *dev, struct drm_file *file) +{ + struct msm_file_private *ctx; + + /* For now, load gpu on open.. to avoid the requirement of having + * firmware in the initrd. + */ + load_gpu(dev); + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + file->driver_priv = ctx; + + return 0; +} + static void msm_preclose(struct drm_device *dev, struct drm_file *file) { struct msm_drm_private *priv = dev->dev_private; + struct msm_file_private *ctx = file->driver_priv; struct msm_kms *kms = priv->kms; + if (kms) kms->funcs->preclose(kms, file); + + mutex_lock(&dev->struct_mutex); + if (ctx == priv->lastctx) + priv->lastctx = NULL; + mutex_unlock(&dev->struct_mutex); + + kfree(ctx); } static void msm_lastclose(struct drm_device *dev) @@ -316,11 +383,30 @@ static void msm_disable_vblank(struct drm_device *dev, int crtc_id) */ #ifdef CONFIG_DEBUG_FS +static int msm_gpu_show(struct drm_device *dev, struct seq_file *m) +{ + struct msm_drm_private *priv = dev->dev_private; + struct msm_gpu *gpu = priv->gpu; + + if (gpu) { + seq_printf(m, "%s Status:\n", gpu->name); + gpu->funcs->show(gpu, m); + } + + return 0; +} + static int msm_gem_show(struct drm_device *dev, struct seq_file *m) { struct msm_drm_private *priv = dev->dev_private; + struct msm_gpu *gpu = priv->gpu; + + if (gpu) { + seq_printf(m, "Active Objects (%s):\n", gpu->name); + msm_gem_describe_objects(&gpu->active_list, m); + } - seq_printf(m, "All Objects:\n"); + seq_printf(m, "Inactive Objects:\n"); msm_gem_describe_objects(&priv->inactive_list, m); return 0; @@ -375,6 +461,7 @@ static int show_locked(struct seq_file *m, void *arg) } static struct drm_info_list msm_debugfs_list[] = { + {"gpu", show_locked, 0, msm_gpu_show}, {"gem", show_locked, 0, msm_gem_show}, { "mm", show_locked, 0, msm_mm_show }, { "fb", show_locked, 0, msm_fb_show }, @@ -404,6 +491,158 @@ static void msm_debugfs_cleanup(struct drm_minor *minor) } #endif +/* + * Fences: + */ + +int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence, + struct timespec *timeout) +{ + struct msm_drm_private *priv = dev->dev_private; + unsigned long timeout_jiffies = timespec_to_jiffies(timeout); + unsigned long start_jiffies = jiffies; + unsigned long remaining_jiffies; + int ret; + + if (time_after(start_jiffies, timeout_jiffies)) + remaining_jiffies = 0; + else + remaining_jiffies = timeout_jiffies - start_jiffies; + + ret = wait_event_interruptible_timeout(priv->fence_event, + priv->completed_fence >= fence, + remaining_jiffies); + if (ret == 0) { + DBG("timeout waiting for fence: %u (completed: %u)", + fence, priv->completed_fence); + ret = -ETIMEDOUT; + } else if (ret != -ERESTARTSYS) { + ret = 0; + } + + return ret; +} + +/* call under struct_mutex */ +void msm_update_fence(struct drm_device *dev, uint32_t fence) +{ + struct msm_drm_private *priv = dev->dev_private; + + if (fence > priv->completed_fence) { + priv->completed_fence = fence; + wake_up_all(&priv->fence_event); + } +} + +/* + * DRM ioctls: + */ + +static int msm_ioctl_get_param(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct msm_drm_private *priv = dev->dev_private; + struct drm_msm_param *args = data; + struct msm_gpu *gpu; + + /* for now, we just have 3d pipe.. eventually this would need to + * be more clever to dispatch to appropriate gpu module: + */ + if (args->pipe != MSM_PIPE_3D0) + return -EINVAL; + + gpu = priv->gpu; + + if (!gpu) + return -ENXIO; + + return gpu->funcs->get_param(gpu, args->param, &args->value); +} + +static int msm_ioctl_gem_new(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_msm_gem_new *args = data; + return msm_gem_new_handle(dev, file, args->size, + args->flags, &args->handle); +} + +#define TS(t) ((struct timespec){ .tv_sec = (t).tv_sec, .tv_nsec = (t).tv_nsec }) + +static int msm_ioctl_gem_cpu_prep(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_msm_gem_cpu_prep *args = data; + struct drm_gem_object *obj; + int ret; + + obj = drm_gem_object_lookup(dev, file, args->handle); + if (!obj) + return -ENOENT; + + ret = msm_gem_cpu_prep(obj, args->op, &TS(args->timeout)); + + drm_gem_object_unreference_unlocked(obj); + + return ret; +} + +static int msm_ioctl_gem_cpu_fini(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_msm_gem_cpu_fini *args = data; + struct drm_gem_object *obj; + int ret; + + obj = drm_gem_object_lookup(dev, file, args->handle); + if (!obj) + return -ENOENT; + + ret = msm_gem_cpu_fini(obj); + + drm_gem_object_unreference_unlocked(obj); + + return ret; +} + +static int msm_ioctl_gem_info(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_msm_gem_info *args = data; + struct drm_gem_object *obj; + int ret = 0; + + if (args->pad) + return -EINVAL; + + obj = drm_gem_object_lookup(dev, file, args->handle); + if (!obj) + return -ENOENT; + + args->offset = msm_gem_mmap_offset(obj); + + drm_gem_object_unreference_unlocked(obj); + + return ret; +} + +static int msm_ioctl_wait_fence(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_msm_wait_fence *args = data; + return msm_wait_fence_interruptable(dev, args->fence, &TS(args->timeout)); +} + +static const struct drm_ioctl_desc msm_ioctls[] = { + DRM_IOCTL_DEF_DRV(MSM_GET_PARAM, msm_ioctl_get_param, DRM_UNLOCKED|DRM_AUTH), + DRM_IOCTL_DEF_DRV(MSM_GEM_NEW, msm_ioctl_gem_new, DRM_UNLOCKED|DRM_AUTH), + DRM_IOCTL_DEF_DRV(MSM_GEM_INFO, msm_ioctl_gem_info, DRM_UNLOCKED|DRM_AUTH), + DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH), + DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH), + DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT, msm_ioctl_gem_submit, DRM_UNLOCKED|DRM_AUTH), + DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE, msm_ioctl_wait_fence, DRM_UNLOCKED|DRM_AUTH), +}; + static const struct vm_operations_struct vm_ops = { .fault = msm_gem_fault, .open = drm_gem_vm_open, @@ -428,6 +667,7 @@ static struct drm_driver msm_driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, .load = msm_load, .unload = msm_unload, + .open = msm_open, .preclose = msm_preclose, .lastclose = msm_lastclose, .irq_handler = msm_irq, @@ -446,6 +686,8 @@ static struct drm_driver msm_driver = { .debugfs_init = msm_debugfs_init, .debugfs_cleanup = msm_debugfs_cleanup, #endif + .ioctls = msm_ioctls, + .num_ioctls = DRM_MSM_NUM_IOCTLS, .fops = &fops, .name = "msm", .desc = "MSM Snapdragon DRM", @@ -514,6 +756,7 @@ static int __init msm_drm_register(void) { DBG("init"); hdmi_register(); + a3xx_register(); return platform_driver_register(&msm_platform_driver); } @@ -522,6 +765,7 @@ static void __exit msm_drm_unregister(void) DBG("fini"); platform_driver_unregister(&msm_platform_driver); hdmi_unregister(); + a3xx_unregister(); } module_init(msm_drm_register); diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 36f8ba2f5c84..34c36b2911d9 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -40,17 +40,34 @@ #include #include #include +#include struct msm_kms; +struct msm_gpu; -#define NUM_DOMAINS 1 /* one for KMS, then one per gpu core (?) */ +#define NUM_DOMAINS 2 /* one for KMS, then one per gpu core (?) */ + +struct msm_file_private { + /* currently we don't do anything useful with this.. but when + * per-context address spaces are supported we'd keep track of + * the context's page-tables here. + */ + int dummy; +}; struct msm_drm_private { struct msm_kms *kms; + /* when we have more than one 'msm_gpu' these need to be an array: */ + struct msm_gpu *gpu; + struct msm_file_private *lastctx; + struct drm_fb_helper *fbdev; + uint32_t next_fence, completed_fence; + wait_queue_head_t fence_event; + /* list of GEM objects: */ struct list_head inactive_list; @@ -108,6 +125,13 @@ int msm_register_iommu(struct drm_device *dev, struct iommu_domain *iommu); int msm_iommu_attach(struct drm_device *dev, struct iommu_domain *iommu, const char **names, int cnt); +int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence, + struct timespec *timeout); +void msm_update_fence(struct drm_device *dev, uint32_t fence); + +int msm_ioctl_gem_submit(struct drm_device *dev, void *data, + struct drm_file *file); + int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma); int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj); @@ -125,6 +149,12 @@ void *msm_gem_vaddr_locked(struct drm_gem_object *obj); void *msm_gem_vaddr(struct drm_gem_object *obj); int msm_gem_queue_inactive_work(struct drm_gem_object *obj, struct work_struct *work); +void msm_gem_move_to_active(struct drm_gem_object *obj, + struct msm_gpu *gpu, uint32_t fence); +void msm_gem_move_to_inactive(struct drm_gem_object *obj); +int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, + struct timespec *timeout); +int msm_gem_cpu_fini(struct drm_gem_object *obj); void msm_gem_free_object(struct drm_gem_object *obj); int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, uint32_t size, uint32_t flags, uint32_t *handle); @@ -168,20 +198,14 @@ static inline int align_pitch(int width, int bpp) /* for the generated headers: */ #define INVALID_IDX(idx) ({BUG(); 0;}) +#define fui(x) ({BUG(); 0;}) +#define util_float_to_half(x) ({BUG(); 0;}) + #define FIELD(val, name) (((val) & name ## __MASK) >> name ## __SHIFT) /* for conditionally setting boolean flag(s): */ #define COND(bool, val) ((bool) ? (val) : 0) -/* just put these here until we start adding driver private ioctls: */ -// TODO might shuffle these around.. just need something for now.. -#define MSM_BO_CACHE_MASK 0x0000000f -#define MSM_BO_SCANOUT 0x00010000 /* scanout capable */ - -#define MSM_BO_CACHED 0x00000001 /* default */ -#define MSM_BO_WC 0x0000002 -#define MSM_BO_UNCACHED 0x00000004 - #endif /* __MSM_DRV_H__ */ diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index a52e6cca8403..6b5a6c8c7658 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -20,6 +20,7 @@ #include "msm_drv.h" #include "msm_gem.h" +#include "msm_gpu.h" /* called with dev->struct_mutex held */ @@ -375,10 +376,74 @@ int msm_gem_queue_inactive_work(struct drm_gem_object *obj, { struct drm_device *dev = obj->dev; struct msm_drm_private *priv = dev->dev_private; + struct msm_gem_object *msm_obj = to_msm_bo(obj); + int ret = 0; + + mutex_lock(&dev->struct_mutex); + if (!list_empty(&work->entry)) { + ret = -EINVAL; + } else if (is_active(msm_obj)) { + list_add_tail(&work->entry, &msm_obj->inactive_work); + } else { + queue_work(priv->wq, work); + } + mutex_unlock(&dev->struct_mutex); + + return ret; +} + +void msm_gem_move_to_active(struct drm_gem_object *obj, + struct msm_gpu *gpu, uint32_t fence) +{ + struct msm_gem_object *msm_obj = to_msm_bo(obj); + msm_obj->gpu = gpu; + msm_obj->fence = fence; + list_del_init(&msm_obj->mm_list); + list_add_tail(&msm_obj->mm_list, &gpu->active_list); +} + +void msm_gem_move_to_inactive(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct msm_drm_private *priv = dev->dev_private; + struct msm_gem_object *msm_obj = to_msm_bo(obj); + + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + + msm_obj->gpu = NULL; + msm_obj->fence = 0; + list_del_init(&msm_obj->mm_list); + list_add_tail(&msm_obj->mm_list, &priv->inactive_list); + + while (!list_empty(&msm_obj->inactive_work)) { + struct work_struct *work; + + work = list_first_entry(&msm_obj->inactive_work, + struct work_struct, entry); + + list_del_init(&work->entry); + queue_work(priv->wq, work); + } +} + +int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, + struct timespec *timeout) +{ + struct drm_device *dev = obj->dev; + struct msm_gem_object *msm_obj = to_msm_bo(obj); + int ret = 0; + + if (is_active(msm_obj) && !(op & MSM_PREP_NOSYNC)) + ret = msm_wait_fence_interruptable(dev, msm_obj->fence, timeout); + + /* TODO cache maintenance */ - /* just a place-holder until we have gpu.. */ - queue_work(priv->wq, work); + return ret; +} +int msm_gem_cpu_fini(struct drm_gem_object *obj) +{ + /* TODO cache maintenance */ return 0; } @@ -390,8 +455,9 @@ void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m) uint64_t off = drm_vma_node_start(&obj->vma_node); WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - seq_printf(m, "%08x: %2d (%2d) %08llx %p %d\n", - msm_obj->flags, obj->name, obj->refcount.refcount.counter, + seq_printf(m, "%08x: %c(%d) %2d (%2d) %08llx %p %d\n", + msm_obj->flags, is_active(msm_obj) ? 'A' : 'I', + msm_obj->fence, obj->name, obj->refcount.refcount.counter, off, msm_obj->vaddr, obj->size); } @@ -421,6 +487,9 @@ void msm_gem_free_object(struct drm_gem_object *obj) WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + /* object should not be on active list: */ + WARN_ON(is_active(msm_obj)); + list_del(&msm_obj->mm_list); for (id = 0; id < ARRAY_SIZE(msm_obj->domain); id++) { @@ -439,6 +508,9 @@ void msm_gem_free_object(struct drm_gem_object *obj) put_pages(obj); + if (msm_obj->resv == &msm_obj->_resv) + reservation_object_fini(msm_obj->resv); + drm_gem_object_release(obj); kfree(msm_obj); @@ -508,7 +580,11 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, msm_obj->flags = flags; + msm_obj->resv = &msm_obj->_resv; + reservation_object_init(msm_obj->resv); + INIT_LIST_HEAD(&msm_obj->submit_entry); + INIT_LIST_HEAD(&msm_obj->inactive_work); list_add_tail(&msm_obj->mm_list, &priv->inactive_list); return obj; diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index fcafd1965151..d746f13d283c 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -18,6 +18,7 @@ #ifndef __MSM_GEM_H__ #define __MSM_GEM_H__ +#include #include "msm_drv.h" struct msm_gem_object { @@ -25,7 +26,27 @@ struct msm_gem_object { uint32_t flags; + /* And object is either: + * inactive - on priv->inactive_list + * active - on one one of the gpu's active_list.. well, at + * least for now we don't have (I don't think) hw sync between + * 2d and 3d one devices which have both, meaning we need to + * block on submit if a bo is already on other ring + * + */ struct list_head mm_list; + struct msm_gpu *gpu; /* non-null if active */ + uint32_t fence; + + /* Transiently in the process of submit ioctl, objects associated + * with the submit are on submit->bo_list.. this only lasts for + * the duration of the ioctl, so one bo can never be on multiple + * submit lists. + */ + struct list_head submit_entry; + + /* work defered until bo is inactive: */ + struct list_head inactive_work; struct page **pages; struct sg_table *sgt; @@ -35,7 +56,44 @@ struct msm_gem_object { // XXX uint32_t iova; } domain[NUM_DOMAINS]; + + /* normally (resv == &_resv) except for imported bo's */ + struct reservation_object *resv; + struct reservation_object _resv; }; #define to_msm_bo(x) container_of(x, struct msm_gem_object, base) +static inline bool is_active(struct msm_gem_object *msm_obj) +{ + return msm_obj->gpu != NULL; +} + +#define MAX_CMDS 4 + +/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc, + * associated with the cmdstream submission for synchronization (and + * make it easier to unwind when things go wrong, etc). This only + * lasts for the duration of the submit-ioctl. + */ +struct msm_gem_submit { + struct drm_device *dev; + struct msm_gpu *gpu; + struct list_head bo_list; + struct ww_acquire_ctx ticket; + uint32_t fence; + bool valid; + unsigned int nr_cmds; + unsigned int nr_bos; + struct { + uint32_t type; + uint32_t size; /* in dwords */ + uint32_t iova; + } cmd[MAX_CMDS]; + struct { + uint32_t flags; + struct msm_gem_object *obj; + uint32_t iova; + } bos[0]; +}; + #endif /* __MSM_GEM_H__ */ diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c new file mode 100644 index 000000000000..3e1ef3a00f60 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "msm_drv.h" +#include "msm_gpu.h" +#include "msm_gem.h" + +/* + * Cmdstream submission: + */ + +#define BO_INVALID_FLAGS ~(MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE) +/* make sure these don't conflict w/ MSM_SUBMIT_BO_x */ +#define BO_VALID 0x8000 +#define BO_LOCKED 0x4000 +#define BO_PINNED 0x2000 + +static inline void __user *to_user_ptr(u64 address) +{ + return (void __user *)(uintptr_t)address; +} + +static struct msm_gem_submit *submit_create(struct drm_device *dev, + struct msm_gpu *gpu, int nr) +{ + struct msm_gem_submit *submit; + int sz = sizeof(*submit) + (nr * sizeof(submit->bos[0])); + + submit = kmalloc(sz, GFP_TEMPORARY | __GFP_NOWARN | __GFP_NORETRY); + if (submit) { + submit->dev = dev; + submit->gpu = gpu; + + /* initially, until copy_from_user() and bo lookup succeeds: */ + submit->nr_bos = 0; + submit->nr_cmds = 0; + + INIT_LIST_HEAD(&submit->bo_list); + ww_acquire_init(&submit->ticket, &reservation_ww_class); + } + + return submit; +} + +static int submit_lookup_objects(struct msm_gem_submit *submit, + struct drm_msm_gem_submit *args, struct drm_file *file) +{ + unsigned i; + int ret = 0; + + spin_lock(&file->table_lock); + + for (i = 0; i < args->nr_bos; i++) { + struct drm_msm_gem_submit_bo submit_bo; + struct drm_gem_object *obj; + struct msm_gem_object *msm_obj; + void __user *userptr = + to_user_ptr(args->bos + (i * sizeof(submit_bo))); + + ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo)); + if (ret) { + ret = -EFAULT; + goto out_unlock; + } + + if (submit_bo.flags & BO_INVALID_FLAGS) { + DBG("invalid flags: %x", submit_bo.flags); + ret = -EINVAL; + goto out_unlock; + } + + submit->bos[i].flags = submit_bo.flags; + /* in validate_objects() we figure out if this is true: */ + submit->bos[i].iova = submit_bo.presumed; + + /* normally use drm_gem_object_lookup(), but for bulk lookup + * all under single table_lock just hit object_idr directly: + */ + obj = idr_find(&file->object_idr, submit_bo.handle); + if (!obj) { + DBG("invalid handle %u at index %u", submit_bo.handle, i); + ret = -EINVAL; + goto out_unlock; + } + + msm_obj = to_msm_bo(obj); + + if (!list_empty(&msm_obj->submit_entry)) { + DBG("handle %u at index %u already on submit list", + submit_bo.handle, i); + ret = -EINVAL; + goto out_unlock; + } + + drm_gem_object_reference(obj); + + submit->bos[i].obj = msm_obj; + + list_add_tail(&msm_obj->submit_entry, &submit->bo_list); + } + +out_unlock: + submit->nr_bos = i; + spin_unlock(&file->table_lock); + + return ret; +} + +static void submit_unlock_unpin_bo(struct msm_gem_submit *submit, int i) +{ + struct msm_gem_object *msm_obj = submit->bos[i].obj; + + if (submit->bos[i].flags & BO_PINNED) + msm_gem_put_iova(&msm_obj->base, submit->gpu->id); + + if (submit->bos[i].flags & BO_LOCKED) + ww_mutex_unlock(&msm_obj->resv->lock); + + if (!(submit->bos[i].flags & BO_VALID)) + submit->bos[i].iova = 0; + + submit->bos[i].flags &= ~(BO_LOCKED | BO_PINNED); +} + +/* This is where we make sure all the bo's are reserved and pin'd: */ +static int submit_validate_objects(struct msm_gem_submit *submit) +{ + int contended, slow_locked = -1, i, ret = 0; + +retry: + submit->valid = true; + + for (i = 0; i < submit->nr_bos; i++) { + struct msm_gem_object *msm_obj = submit->bos[i].obj; + uint32_t iova; + + if (slow_locked == i) + slow_locked = -1; + + contended = i; + + if (!(submit->bos[i].flags & BO_LOCKED)) { + ret = ww_mutex_lock_interruptible(&msm_obj->resv->lock, + &submit->ticket); + if (ret) + goto fail; + submit->bos[i].flags |= BO_LOCKED; + } + + + /* if locking succeeded, pin bo: */ + ret = msm_gem_get_iova(&msm_obj->base, + submit->gpu->id, &iova); + + /* this would break the logic in the fail path.. there is no + * reason for this to happen, but just to be on the safe side + * let's notice if this starts happening in the future: + */ + WARN_ON(ret == -EDEADLK); + + if (ret) + goto fail; + + submit->bos[i].flags |= BO_PINNED; + + if (iova == submit->bos[i].iova) { + submit->bos[i].flags |= BO_VALID; + } else { + submit->bos[i].iova = iova; + submit->bos[i].flags &= ~BO_VALID; + submit->valid = false; + } + } + + ww_acquire_done(&submit->ticket); + + return 0; + +fail: + for (; i >= 0; i--) + submit_unlock_unpin_bo(submit, i); + + if (slow_locked > 0) + submit_unlock_unpin_bo(submit, slow_locked); + + if (ret == -EDEADLK) { + struct msm_gem_object *msm_obj = submit->bos[contended].obj; + /* we lost out in a seqno race, lock and retry.. */ + ret = ww_mutex_lock_slow_interruptible(&msm_obj->resv->lock, + &submit->ticket); + if (!ret) { + submit->bos[contended].flags |= BO_LOCKED; + slow_locked = contended; + goto retry; + } + } + + return ret; +} + +static int submit_bo(struct msm_gem_submit *submit, uint32_t idx, + struct msm_gem_object **obj, uint32_t *iova, bool *valid) +{ + if (idx >= submit->nr_bos) { + DBG("invalid buffer index: %u (out of %u)", idx, submit->nr_bos); + return EINVAL; + } + + if (obj) + *obj = submit->bos[idx].obj; + if (iova) + *iova = submit->bos[idx].iova; + if (valid) + *valid = !!(submit->bos[idx].flags & BO_VALID); + + return 0; +} + +/* process the reloc's and patch up the cmdstream as needed: */ +static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *obj, + uint32_t offset, uint32_t nr_relocs, uint64_t relocs) +{ + uint32_t i, last_offset = 0; + uint32_t *ptr; + int ret; + + if (offset % 4) { + DBG("non-aligned cmdstream buffer: %u", offset); + return -EINVAL; + } + + /* For now, just map the entire thing. Eventually we probably + * to do it page-by-page, w/ kmap() if not vmap()d.. + */ + ptr = msm_gem_vaddr(&obj->base); + + if (IS_ERR(ptr)) { + ret = PTR_ERR(ptr); + DBG("failed to map: %d", ret); + return ret; + } + + for (i = 0; i < nr_relocs; i++) { + struct drm_msm_gem_submit_reloc submit_reloc; + void __user *userptr = + to_user_ptr(relocs + (i * sizeof(submit_reloc))); + uint32_t iova, off; + bool valid; + + ret = copy_from_user(&submit_reloc, userptr, sizeof(submit_reloc)); + if (ret) + return -EFAULT; + + if (submit_reloc.submit_offset % 4) { + DBG("non-aligned reloc offset: %u", + submit_reloc.submit_offset); + return -EINVAL; + } + + /* offset in dwords: */ + off = submit_reloc.submit_offset / 4; + + if ((off >= (obj->base.size / 4)) || + (off < last_offset)) { + DBG("invalid offset %u at reloc %u", off, i); + return -EINVAL; + } + + ret = submit_bo(submit, submit_reloc.reloc_idx, NULL, &iova, &valid); + if (ret) + return ret; + + if (valid) + continue; + + iova += submit_reloc.reloc_offset; + + if (submit_reloc.shift < 0) + iova >>= -submit_reloc.shift; + else + iova <<= submit_reloc.shift; + + ptr[off] = iova | submit_reloc.or; + + last_offset = off; + } + + return 0; +} + +static void submit_cleanup(struct msm_gem_submit *submit, bool fail) +{ + unsigned i; + + mutex_lock(&submit->dev->struct_mutex); + for (i = 0; i < submit->nr_bos; i++) { + struct msm_gem_object *msm_obj = submit->bos[i].obj; + submit_unlock_unpin_bo(submit, i); + list_del_init(&msm_obj->submit_entry); + drm_gem_object_unreference(&msm_obj->base); + } + mutex_unlock(&submit->dev->struct_mutex); + + ww_acquire_fini(&submit->ticket); + kfree(submit); +} + +int msm_ioctl_gem_submit(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct msm_drm_private *priv = dev->dev_private; + struct drm_msm_gem_submit *args = data; + struct msm_file_private *ctx = file->driver_priv; + struct msm_gem_submit *submit; + struct msm_gpu *gpu; + unsigned i; + int ret; + + /* for now, we just have 3d pipe.. eventually this would need to + * be more clever to dispatch to appropriate gpu module: + */ + if (args->pipe != MSM_PIPE_3D0) + return -EINVAL; + + gpu = priv->gpu; + + if (args->nr_cmds > MAX_CMDS) + return -EINVAL; + + submit = submit_create(dev, gpu, args->nr_bos); + if (!submit) { + ret = -ENOMEM; + goto out; + } + + ret = submit_lookup_objects(submit, args, file); + if (ret) + goto out; + + ret = submit_validate_objects(submit); + if (ret) + goto out; + + for (i = 0; i < args->nr_cmds; i++) { + struct drm_msm_gem_submit_cmd submit_cmd; + void __user *userptr = + to_user_ptr(args->cmds + (i * sizeof(submit_cmd))); + struct msm_gem_object *msm_obj; + uint32_t iova; + + ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd)); + if (ret) { + ret = -EFAULT; + goto out; + } + + ret = submit_bo(submit, submit_cmd.submit_idx, + &msm_obj, &iova, NULL); + if (ret) + goto out; + + if (submit_cmd.size % 4) { + DBG("non-aligned cmdstream buffer size: %u", + submit_cmd.size); + ret = -EINVAL; + goto out; + } + + if (submit_cmd.size >= msm_obj->base.size) { + DBG("invalid cmdstream size: %u", submit_cmd.size); + ret = -EINVAL; + goto out; + } + + submit->cmd[i].type = submit_cmd.type; + submit->cmd[i].size = submit_cmd.size / 4; + submit->cmd[i].iova = iova + submit_cmd.submit_offset; + + if (submit->valid) + continue; + + ret = submit_reloc(submit, msm_obj, submit_cmd.submit_offset, + submit_cmd.nr_relocs, submit_cmd.relocs); + if (ret) + goto out; + } + + submit->nr_cmds = i; + + ret = msm_gpu_submit(gpu, submit, ctx); + + args->fence = submit->fence; + +out: + if (submit) + submit_cleanup(submit, !!ret); + return ret; +} diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c new file mode 100644 index 000000000000..7c6541e4a7ec --- /dev/null +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "msm_gpu.h" +#include "msm_gem.h" + + +/* + * Power Management: + */ + +#ifdef CONFIG_MSM_BUS_SCALING +#include +#include +static void bs_init(struct msm_gpu *gpu, struct platform_device *pdev) +{ + struct drm_device *dev = gpu->dev; + struct kgsl_device_platform_data *pdata = pdev->dev.platform_data; + + if (!pdev) { + dev_err(dev->dev, "could not find dtv pdata\n"); + return; + } + + if (pdata->bus_scale_table) { + gpu->bsc = msm_bus_scale_register_client(pdata->bus_scale_table); + DBG("bus scale client: %08x", gpu->bsc); + } +} + +static void bs_fini(struct msm_gpu *gpu) +{ + if (gpu->bsc) { + msm_bus_scale_unregister_client(gpu->bsc); + gpu->bsc = 0; + } +} + +static void bs_set(struct msm_gpu *gpu, int idx) +{ + if (gpu->bsc) { + DBG("set bus scaling: %d", idx); + msm_bus_scale_client_update_request(gpu->bsc, idx); + } +} +#else +static void bs_init(struct msm_gpu *gpu, struct platform_device *pdev) {} +static void bs_fini(struct msm_gpu *gpu) {} +static void bs_set(struct msm_gpu *gpu, int idx) {} +#endif + +static int enable_pwrrail(struct msm_gpu *gpu) +{ + struct drm_device *dev = gpu->dev; + int ret = 0; + + if (gpu->gpu_reg) { + ret = regulator_enable(gpu->gpu_reg); + if (ret) { + dev_err(dev->dev, "failed to enable 'gpu_reg': %d\n", ret); + return ret; + } + } + + if (gpu->gpu_cx) { + ret = regulator_enable(gpu->gpu_cx); + if (ret) { + dev_err(dev->dev, "failed to enable 'gpu_cx': %d\n", ret); + return ret; + } + } + + return 0; +} + +static int disable_pwrrail(struct msm_gpu *gpu) +{ + if (gpu->gpu_cx) + regulator_disable(gpu->gpu_cx); + if (gpu->gpu_reg) + regulator_disable(gpu->gpu_reg); + return 0; +} + +static int enable_clk(struct msm_gpu *gpu) +{ + struct clk *rate_clk = NULL; + int i; + + /* NOTE: kgsl_pwrctrl_clk() ignores grp_clks[0].. */ + for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--) { + if (gpu->grp_clks[i]) { + clk_prepare(gpu->grp_clks[i]); + rate_clk = gpu->grp_clks[i]; + } + } + + if (rate_clk && gpu->fast_rate) + clk_set_rate(rate_clk, gpu->fast_rate); + + for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--) + if (gpu->grp_clks[i]) + clk_enable(gpu->grp_clks[i]); + + return 0; +} + +static int disable_clk(struct msm_gpu *gpu) +{ + struct clk *rate_clk = NULL; + int i; + + /* NOTE: kgsl_pwrctrl_clk() ignores grp_clks[0].. */ + for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--) { + if (gpu->grp_clks[i]) { + clk_disable(gpu->grp_clks[i]); + rate_clk = gpu->grp_clks[i]; + } + } + + if (rate_clk && gpu->slow_rate) + clk_set_rate(rate_clk, gpu->slow_rate); + + for (i = ARRAY_SIZE(gpu->grp_clks) - 1; i > 0; i--) + if (gpu->grp_clks[i]) + clk_unprepare(gpu->grp_clks[i]); + + return 0; +} + +static int enable_axi(struct msm_gpu *gpu) +{ + if (gpu->ebi1_clk) + clk_prepare_enable(gpu->ebi1_clk); + if (gpu->bus_freq) + bs_set(gpu, gpu->bus_freq); + return 0; +} + +static int disable_axi(struct msm_gpu *gpu) +{ + if (gpu->ebi1_clk) + clk_disable_unprepare(gpu->ebi1_clk); + if (gpu->bus_freq) + bs_set(gpu, 0); + return 0; +} + +int msm_gpu_pm_resume(struct msm_gpu *gpu) +{ + int ret; + + DBG("%s", gpu->name); + + ret = enable_pwrrail(gpu); + if (ret) + return ret; + + ret = enable_clk(gpu); + if (ret) + return ret; + + ret = enable_axi(gpu); + if (ret) + return ret; + + return 0; +} + +int msm_gpu_pm_suspend(struct msm_gpu *gpu) +{ + int ret; + + DBG("%s", gpu->name); + + ret = disable_axi(gpu); + if (ret) + return ret; + + ret = disable_clk(gpu); + if (ret) + return ret; + + ret = disable_pwrrail(gpu); + if (ret) + return ret; + + return 0; +} + +/* + * Cmdstream submission/retirement: + */ + +static void retire_worker(struct work_struct *work) +{ + struct msm_gpu *gpu = container_of(work, struct msm_gpu, retire_work); + struct drm_device *dev = gpu->dev; + uint32_t fence = gpu->funcs->last_fence(gpu); + + mutex_lock(&dev->struct_mutex); + + while (!list_empty(&gpu->active_list)) { + struct msm_gem_object *obj; + + obj = list_first_entry(&gpu->active_list, + struct msm_gem_object, mm_list); + + if (obj->fence <= fence) { + /* move to inactive: */ + msm_gem_move_to_inactive(&obj->base); + msm_gem_put_iova(&obj->base, gpu->id); + drm_gem_object_unreference(&obj->base); + } else { + break; + } + } + + msm_update_fence(gpu->dev, fence); + + mutex_unlock(&dev->struct_mutex); +} + +/* call from irq handler to schedule work to retire bo's */ +void msm_gpu_retire(struct msm_gpu *gpu) +{ + struct msm_drm_private *priv = gpu->dev->dev_private; + queue_work(priv->wq, &gpu->retire_work); +} + +/* add bo's to gpu's ring, and kick gpu: */ +int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, + struct msm_file_private *ctx) +{ + struct drm_device *dev = gpu->dev; + struct msm_drm_private *priv = dev->dev_private; + int i, ret; + + mutex_lock(&dev->struct_mutex); + + submit->fence = ++priv->next_fence; + + ret = gpu->funcs->submit(gpu, submit, ctx); + priv->lastctx = ctx; + + for (i = 0; i < submit->nr_bos; i++) { + struct msm_gem_object *msm_obj = submit->bos[i].obj; + + /* can't happen yet.. but when we add 2d support we'll have + * to deal w/ cross-ring synchronization: + */ + WARN_ON(is_active(msm_obj) && (msm_obj->gpu != gpu)); + + if (!is_active(msm_obj)) { + uint32_t iova; + + /* ring takes a reference to the bo and iova: */ + drm_gem_object_reference(&msm_obj->base); + msm_gem_get_iova_locked(&msm_obj->base, + submit->gpu->id, &iova); + } + + msm_gem_move_to_active(&msm_obj->base, gpu, submit->fence); + } + mutex_unlock(&dev->struct_mutex); + + return ret; +} + +/* + * Init/Cleanup: + */ + +static irqreturn_t irq_handler(int irq, void *data) +{ + struct msm_gpu *gpu = data; + return gpu->funcs->irq(gpu); +} + +static const char *clk_names[] = { + "src_clk", "core_clk", "iface_clk", "mem_clk", "mem_iface_clk", +}; + +int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, + struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs, + const char *name, const char *ioname, const char *irqname, int ringsz) +{ + int i, ret; + + gpu->dev = drm; + gpu->funcs = funcs; + gpu->name = name; + + INIT_LIST_HEAD(&gpu->active_list); + INIT_WORK(&gpu->retire_work, retire_worker); + + BUG_ON(ARRAY_SIZE(clk_names) != ARRAY_SIZE(gpu->grp_clks)); + + /* Map registers: */ + gpu->mmio = msm_ioremap(pdev, ioname, name); + if (IS_ERR(gpu->mmio)) { + ret = PTR_ERR(gpu->mmio); + goto fail; + } + + /* Get Interrupt: */ + gpu->irq = platform_get_irq_byname(pdev, irqname); + if (gpu->irq < 0) { + ret = gpu->irq; + dev_err(drm->dev, "failed to get irq: %d\n", ret); + goto fail; + } + + ret = devm_request_irq(&pdev->dev, gpu->irq, irq_handler, + IRQF_TRIGGER_HIGH, gpu->name, gpu); + if (ret) { + dev_err(drm->dev, "failed to request IRQ%u: %d\n", gpu->irq, ret); + goto fail; + } + + /* Acquire clocks: */ + for (i = 0; i < ARRAY_SIZE(clk_names); i++) { + gpu->grp_clks[i] = devm_clk_get(&pdev->dev, clk_names[i]); + DBG("grp_clks[%s]: %p", clk_names[i], gpu->grp_clks[i]); + if (IS_ERR(gpu->grp_clks[i])) + gpu->grp_clks[i] = NULL; + } + + gpu->ebi1_clk = devm_clk_get(&pdev->dev, "bus_clk"); + DBG("ebi1_clk: %p", gpu->ebi1_clk); + if (IS_ERR(gpu->ebi1_clk)) + gpu->ebi1_clk = NULL; + + /* Acquire regulators: */ + gpu->gpu_reg = devm_regulator_get(&pdev->dev, "vdd"); + DBG("gpu_reg: %p", gpu->gpu_reg); + if (IS_ERR(gpu->gpu_reg)) + gpu->gpu_reg = NULL; + + gpu->gpu_cx = devm_regulator_get(&pdev->dev, "vddcx"); + DBG("gpu_cx: %p", gpu->gpu_cx); + if (IS_ERR(gpu->gpu_cx)) + gpu->gpu_cx = NULL; + + /* Setup IOMMU.. eventually we will (I think) do this once per context + * and have separate page tables per context. For now, to keep things + * simple and to get something working, just use a single address space: + */ + gpu->iommu = iommu_domain_alloc(&platform_bus_type); + if (!gpu->iommu) { + dev_err(drm->dev, "failed to allocate IOMMU\n"); + ret = -ENOMEM; + goto fail; + } + gpu->id = msm_register_iommu(drm, gpu->iommu); + + /* Create ringbuffer: */ + gpu->rb = msm_ringbuffer_new(gpu, ringsz); + if (IS_ERR(gpu->rb)) { + ret = PTR_ERR(gpu->rb); + gpu->rb = NULL; + dev_err(drm->dev, "could not create ringbuffer: %d\n", ret); + goto fail; + } + + ret = msm_gem_get_iova_locked(gpu->rb->bo, gpu->id, &gpu->rb_iova); + if (ret) { + gpu->rb_iova = 0; + dev_err(drm->dev, "could not map ringbuffer: %d\n", ret); + goto fail; + } + + bs_init(gpu, pdev); + + return 0; + +fail: + return ret; +} + +void msm_gpu_cleanup(struct msm_gpu *gpu) +{ + DBG("%s", gpu->name); + + WARN_ON(!list_empty(&gpu->active_list)); + + bs_fini(gpu); + + if (gpu->rb) { + if (gpu->rb_iova) + msm_gem_put_iova(gpu->rb->bo, gpu->id); + msm_ringbuffer_destroy(gpu->rb); + } + + if (gpu->iommu) + iommu_domain_free(gpu->iommu); +} diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h new file mode 100644 index 000000000000..8d2cd6c2226b --- /dev/null +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef __MSM_GPU_H__ +#define __MSM_GPU_H__ + +#include +#include + +#include "msm_drv.h" +#include "msm_ringbuffer.h" + +struct msm_gem_submit; + +/* So far, with hardware that I've seen to date, we can have: + * + zero, one, or two z180 2d cores + * + a3xx or a2xx 3d core, which share a common CP (the firmware + * for the CP seems to implement some different PM4 packet types + * but the basics of cmdstream submission are the same) + * + * Which means that the eventual complete "class" hierarchy, once + * support for all past and present hw is in place, becomes: + * + msm_gpu + * + adreno_gpu + * + a3xx_gpu + * + a2xx_gpu + * + z180_gpu + */ +struct msm_gpu_funcs { + int (*get_param)(struct msm_gpu *gpu, uint32_t param, uint64_t *value); + int (*hw_init)(struct msm_gpu *gpu); + int (*pm_suspend)(struct msm_gpu *gpu); + int (*pm_resume)(struct msm_gpu *gpu); + int (*submit)(struct msm_gpu *gpu, struct msm_gem_submit *submit, + struct msm_file_private *ctx); + void (*flush)(struct msm_gpu *gpu); + void (*idle)(struct msm_gpu *gpu); + irqreturn_t (*irq)(struct msm_gpu *irq); + uint32_t (*last_fence)(struct msm_gpu *gpu); + void (*destroy)(struct msm_gpu *gpu); +#ifdef CONFIG_DEBUG_FS + /* show GPU status in debugfs: */ + void (*show)(struct msm_gpu *gpu, struct seq_file *m); +#endif +}; + +struct msm_gpu { + const char *name; + struct drm_device *dev; + const struct msm_gpu_funcs *funcs; + + struct msm_ringbuffer *rb; + uint32_t rb_iova; + + /* list of GEM active objects: */ + struct list_head active_list; + + /* worker for handling active-list retiring: */ + struct work_struct retire_work; + + void __iomem *mmio; + int irq; + + struct iommu_domain *iommu; + int id; + + /* Power Control: */ + struct regulator *gpu_reg, *gpu_cx; + struct clk *ebi1_clk, *grp_clks[5]; + uint32_t fast_rate, slow_rate, bus_freq; + uint32_t bsc; +}; + +static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data) +{ + msm_writel(data, gpu->mmio + (reg << 2)); +} + +static inline u32 gpu_read(struct msm_gpu *gpu, u32 reg) +{ + return msm_readl(gpu->mmio + (reg << 2)); +} + +int msm_gpu_pm_suspend(struct msm_gpu *gpu); +int msm_gpu_pm_resume(struct msm_gpu *gpu); + +void msm_gpu_retire(struct msm_gpu *gpu); +int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, + struct msm_file_private *ctx); + +int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, + struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs, + const char *name, const char *ioname, const char *irqname, int ringsz); +void msm_gpu_cleanup(struct msm_gpu *gpu); + +struct msm_gpu *a3xx_gpu_init(struct drm_device *dev); +void __init a3xx_register(void); +void __exit a3xx_unregister(void); + +#endif /* __MSM_GPU_H__ */ diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c new file mode 100644 index 000000000000..8171537dd7d1 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_ringbuffer.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "msm_ringbuffer.h" +#include "msm_gpu.h" + +struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int size) +{ + struct msm_ringbuffer *ring; + int ret; + + size = ALIGN(size, 4); /* size should be dword aligned */ + + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) { + ret = -ENOMEM; + goto fail; + } + + ring->gpu = gpu; + ring->bo = msm_gem_new(gpu->dev, size, MSM_BO_WC); + if (IS_ERR(ring->bo)) { + ret = PTR_ERR(ring->bo); + ring->bo = NULL; + goto fail; + } + + ring->start = msm_gem_vaddr_locked(ring->bo); + ring->end = ring->start + (size / 4); + ring->cur = ring->start; + + ring->size = size; + + return ring; + +fail: + if (ring) + msm_ringbuffer_destroy(ring); + return ERR_PTR(ret); +} + +void msm_ringbuffer_destroy(struct msm_ringbuffer *ring) +{ + if (ring->bo) + drm_gem_object_unreference(ring->bo); + kfree(ring); +} diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.h b/drivers/gpu/drm/msm/msm_ringbuffer.h new file mode 100644 index 000000000000..6e0e1049fa4f --- /dev/null +++ b/drivers/gpu/drm/msm/msm_ringbuffer.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef __MSM_RINGBUFFER_H__ +#define __MSM_RINGBUFFER_H__ + +#include "msm_drv.h" + +struct msm_ringbuffer { + struct msm_gpu *gpu; + int size; + struct drm_gem_object *bo; + uint32_t *start, *end, *cur; +}; + +struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int size); +void msm_ringbuffer_destroy(struct msm_ringbuffer *ring); + +/* ringbuffer helpers (the parts that are same for a3xx/a2xx/z180..) */ + +static inline void +OUT_RING(struct msm_ringbuffer *ring, uint32_t data) +{ + if (ring->cur == ring->end) + ring->cur = ring->start; + *(ring->cur++) = data; +} + +#endif /* __MSM_RINGBUFFER_H__ */ diff --git a/include/uapi/drm/Kbuild b/include/uapi/drm/Kbuild index 119487e05e65..2d9a25daab05 100644 --- a/include/uapi/drm/Kbuild +++ b/include/uapi/drm/Kbuild @@ -16,3 +16,4 @@ header-y += sis_drm.h header-y += tegra_drm.h header-y += via_drm.h header-y += vmwgfx_drm.h +header-y += msm_drm.h diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h new file mode 100644 index 000000000000..d3c62074016d --- /dev/null +++ b/include/uapi/drm/msm_drm.h @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef __MSM_DRM_H__ +#define __MSM_DRM_H__ + +#include +#include + +/* Please note that modifications to all structs defined here are + * subject to backwards-compatibility constraints: + * 1) Do not use pointers, use uint64_t instead for 32 bit / 64 bit + * user/kernel compatibility + * 2) Keep fields aligned to their size + * 3) Because of how drm_ioctl() works, we can add new fields at + * the end of an ioctl if some care is taken: drm_ioctl() will + * zero out the new fields at the tail of the ioctl, so a zero + * value should have a backwards compatible meaning. And for + * output params, userspace won't see the newly added output + * fields.. so that has to be somehow ok. + */ + +#define MSM_PIPE_NONE 0x00 +#define MSM_PIPE_2D0 0x01 +#define MSM_PIPE_2D1 0x02 +#define MSM_PIPE_3D0 0x10 + +/* timeouts are specified in clock-monotonic absolute times (to simplify + * restarting interrupted ioctls). The following struct is logically the + * same as 'struct timespec' but 32/64b ABI safe. + */ +struct drm_msm_timespec { + int64_t tv_sec; /* seconds */ + int64_t tv_nsec; /* nanoseconds */ +}; + +#define MSM_PARAM_GPU_ID 0x01 +#define MSM_PARAM_GMEM_SIZE 0x02 + +struct drm_msm_param { + uint32_t pipe; /* in, MSM_PIPE_x */ + uint32_t param; /* in, MSM_PARAM_x */ + uint64_t value; /* out (get_param) or in (set_param) */ +}; + +/* + * GEM buffers: + */ + +#define MSM_BO_SCANOUT 0x00000001 /* scanout capable */ +#define MSM_BO_GPU_READONLY 0x00000002 +#define MSM_BO_CACHE_MASK 0x000f0000 +/* cache modes */ +#define MSM_BO_CACHED 0x00010000 +#define MSM_BO_WC 0x00020000 +#define MSM_BO_UNCACHED 0x00040000 + +struct drm_msm_gem_new { + uint64_t size; /* in */ + uint32_t flags; /* in, mask of MSM_BO_x */ + uint32_t handle; /* out */ +}; + +struct drm_msm_gem_info { + uint32_t handle; /* in */ + uint32_t pad; + uint64_t offset; /* out, offset to pass to mmap() */ +}; + +#define MSM_PREP_READ 0x01 +#define MSM_PREP_WRITE 0x02 +#define MSM_PREP_NOSYNC 0x04 + +struct drm_msm_gem_cpu_prep { + uint32_t handle; /* in */ + uint32_t op; /* in, mask of MSM_PREP_x */ + struct drm_msm_timespec timeout; /* in */ +}; + +struct drm_msm_gem_cpu_fini { + uint32_t handle; /* in */ +}; + +/* + * Cmdstream Submission: + */ + +/* The value written into the cmdstream is logically: + * + * ((relocbuf->gpuaddr + reloc_offset) << shift) | or + * + * When we have GPU's w/ >32bit ptrs, it should be possible to deal + * with this by emit'ing two reloc entries with appropriate shift + * values. Or a new MSM_SUBMIT_CMD_x type would also be an option. + * + * NOTE that reloc's must be sorted by order of increasing submit_offset, + * otherwise EINVAL. + */ +struct drm_msm_gem_submit_reloc { + uint32_t submit_offset; /* in, offset from submit_bo */ + uint32_t or; /* in, value OR'd with result */ + int32_t shift; /* in, amount of left shift (can be negative) */ + uint32_t reloc_idx; /* in, index of reloc_bo buffer */ + uint64_t reloc_offset; /* in, offset from start of reloc_bo */ +}; + +/* submit-types: + * BUF - this cmd buffer is executed normally. + * IB_TARGET_BUF - this cmd buffer is an IB target. Reloc's are + * processed normally, but the kernel does not setup an IB to + * this buffer in the first-level ringbuffer + * CTX_RESTORE_BUF - only executed if there has been a GPU context + * switch since the last SUBMIT ioctl + */ +#define MSM_SUBMIT_CMD_BUF 0x0001 +#define MSM_SUBMIT_CMD_IB_TARGET_BUF 0x0002 +#define MSM_SUBMIT_CMD_CTX_RESTORE_BUF 0x0003 +struct drm_msm_gem_submit_cmd { + uint32_t type; /* in, one of MSM_SUBMIT_CMD_x */ + uint32_t submit_idx; /* in, index of submit_bo cmdstream buffer */ + uint32_t submit_offset; /* in, offset into submit_bo */ + uint32_t size; /* in, cmdstream size */ + uint32_t pad; + uint32_t nr_relocs; /* in, number of submit_reloc's */ + uint64_t __user relocs; /* in, ptr to array of submit_reloc's */ +}; + +/* Each buffer referenced elsewhere in the cmdstream submit (ie. the + * cmdstream buffer(s) themselves or reloc entries) has one (and only + * one) entry in the submit->bos[] table. + * + * As a optimization, the current buffer (gpu virtual address) can be + * passed back through the 'presumed' field. If on a subsequent reloc, + * userspace passes back a 'presumed' address that is still valid, + * then patching the cmdstream for this entry is skipped. This can + * avoid kernel needing to map/access the cmdstream bo in the common + * case. + */ +#define MSM_SUBMIT_BO_READ 0x0001 +#define MSM_SUBMIT_BO_WRITE 0x0002 +struct drm_msm_gem_submit_bo { + uint32_t flags; /* in, mask of MSM_SUBMIT_BO_x */ + uint32_t handle; /* in, GEM handle */ + uint64_t presumed; /* in/out, presumed buffer address */ +}; + +/* Each cmdstream submit consists of a table of buffers involved, and + * one or more cmdstream buffers. This allows for conditional execution + * (context-restore), and IB buffers needed for per tile/bin draw cmds. + */ +struct drm_msm_gem_submit { + uint32_t pipe; /* in, MSM_PIPE_x */ + uint32_t fence; /* out */ + uint32_t nr_bos; /* in, number of submit_bo's */ + uint32_t nr_cmds; /* in, number of submit_cmd's */ + uint64_t __user bos; /* in, ptr to array of submit_bo's */ + uint64_t __user cmds; /* in, ptr to array of submit_cmd's */ +}; + +/* The normal way to synchronize with the GPU is just to CPU_PREP on + * a buffer if you need to access it from the CPU (other cmdstream + * submission from same or other contexts, PAGE_FLIP ioctl, etc, all + * handle the required synchronization under the hood). This ioctl + * mainly just exists as a way to implement the gallium pipe_fence + * APIs without requiring a dummy bo to synchronize on. + */ +struct drm_msm_wait_fence { + uint32_t fence; /* in */ + uint32_t pad; + struct drm_msm_timespec timeout; /* in */ +}; + +#define DRM_MSM_GET_PARAM 0x00 +/* placeholder: +#define DRM_MSM_SET_PARAM 0x01 + */ +#define DRM_MSM_GEM_NEW 0x02 +#define DRM_MSM_GEM_INFO 0x03 +#define DRM_MSM_GEM_CPU_PREP 0x04 +#define DRM_MSM_GEM_CPU_FINI 0x05 +#define DRM_MSM_GEM_SUBMIT 0x06 +#define DRM_MSM_WAIT_FENCE 0x07 +#define DRM_MSM_NUM_IOCTLS 0x08 + +#define DRM_IOCTL_MSM_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GET_PARAM, struct drm_msm_param) +#define DRM_IOCTL_MSM_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_NEW, struct drm_msm_gem_new) +#define DRM_IOCTL_MSM_GEM_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_INFO, struct drm_msm_gem_info) +#define DRM_IOCTL_MSM_GEM_CPU_PREP DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_GEM_CPU_PREP, struct drm_msm_gem_cpu_prep) +#define DRM_IOCTL_MSM_GEM_CPU_FINI DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_GEM_CPU_FINI, struct drm_msm_gem_cpu_fini) +#define DRM_IOCTL_MSM_GEM_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_SUBMIT, struct drm_msm_gem_submit) +#define DRM_IOCTL_MSM_WAIT_FENCE DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_WAIT_FENCE, struct drm_msm_wait_fence) + +#endif /* __MSM_DRM_H__ */ -- cgit v1.2.3-71-gd317 From 214fc309d1387e822d606a33a10e31cacfe83520 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 8 Aug 2013 12:38:31 +0200 Subject: mmc: slot-gpio: Add debouncing capability to mmc_gpio_request_cd() Add a debounce parameter to the mmc_gpio_request_cd() function that enables GPIO debouncing when set to a non-zero value. This can be used by MMC host drivers to enable debouncing on the card detect signal. Signed-off-by: Laurent Pinchart Reviewed-by: H Hartley Sweeten Signed-off-by: Chris Ball --- drivers/mmc/core/host.c | 2 +- drivers/mmc/core/slot-gpio.c | 14 +++++++++++++- drivers/mmc/host/jz4740_mmc.c | 2 +- drivers/mmc/host/mvsdio.c | 3 ++- drivers/mmc/host/sdhci-esdhc-imx.c | 2 +- drivers/mmc/host/sdhci-pxav3.c | 3 ++- drivers/mmc/host/sdhci-sirf.c | 2 +- drivers/mmc/host/sh_mmcif.c | 2 +- drivers/mmc/host/tmio_mmc_pio.c | 2 +- include/linux/mmc/slot-gpio.h | 3 ++- 10 files changed, 25 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 6fb6f77450cb..49bc403e31f0 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -374,7 +374,7 @@ int mmc_of_parse(struct mmc_host *host) if (!(flags & OF_GPIO_ACTIVE_LOW)) gpio_inv_cd = true; - ret = mmc_gpio_request_cd(host, gpio); + ret = mmc_gpio_request_cd(host, gpio, 0); if (ret < 0) { dev_err(host->parent, "Failed to request CD GPIO #%d: %d!\n", diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 324235105519..46596b71a32f 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -135,6 +135,7 @@ EXPORT_SYMBOL(mmc_gpio_request_ro); * mmc_gpio_request_cd - request a gpio for card-detection * @host: mmc host * @gpio: gpio number requested + * @debounce: debounce time in microseconds * * As devm_* managed functions are used in mmc_gpio_request_cd(), client * drivers do not need to explicitly call mmc_gpio_free_cd() for freeing up, @@ -143,9 +144,14 @@ EXPORT_SYMBOL(mmc_gpio_request_ro); * switching for card-detection, they are responsible for calling * mmc_gpio_request_cd() and mmc_gpio_free_cd() as a pair on their own. * + * If GPIO debouncing is desired, set the debounce parameter to a non-zero + * value. The caller is responsible for ensuring that the GPIO driver associated + * with the GPIO supports debouncing, otherwise an error will be returned. + * * Returns zero on success, else an error. */ -int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) +int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, + unsigned int debounce) { struct mmc_gpio *ctx; int irq = gpio_to_irq(gpio); @@ -167,6 +173,12 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) */ return ret; + if (debounce) { + ret = gpio_set_debounce(gpio, debounce); + if (ret < 0) + return ret; + } + /* * Even if gpio_to_irq() returns a valid IRQ number, the platform might * still prefer to poll, e.g., because that IRQ number is already used diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index d058f0c85f43..66516339e3a0 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -713,7 +713,7 @@ static int jz4740_mmc_request_gpios(struct mmc_host *mmc, mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; if (gpio_is_valid(pdata->gpio_card_detect)) { - ret = mmc_gpio_request_cd(mmc, pdata->gpio_card_detect); + ret = mmc_gpio_request_cd(mmc, pdata->gpio_card_detect, 0); if (ret) return ret; } diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 4ddd83f98658..06c5b0b28ebc 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -757,7 +757,8 @@ static int __init mvsd_probe(struct platform_device *pdev) if (mvsd_data->gpio_card_detect && gpio_is_valid(mvsd_data->gpio_card_detect)) { ret = mmc_gpio_request_cd(mmc, - mvsd_data->gpio_card_detect); + mvsd_data->gpio_card_detect, + 0); if (ret) goto out; } else { diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 1dd5ba858754..abc8cf01e6e3 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -616,7 +616,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) /* card_detect */ switch (boarddata->cd_type) { case ESDHC_CD_GPIO: - err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio); + err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio, 0); if (err) { dev_err(mmc_dev(host->mmc), "failed to request card-detect gpio!\n"); diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index bf99359a3a90..793dacd3b841 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -278,7 +278,8 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) host->mmc->pm_caps |= pdata->pm_caps; if (gpio_is_valid(pdata->ext_cd_gpio)) { - ret = mmc_gpio_request_cd(host->mmc, pdata->ext_cd_gpio); + ret = mmc_gpio_request_cd(host->mmc, pdata->ext_cd_gpio, + 0); if (ret) { dev_err(mmc_dev(host->mmc), "failed to allocate card detect gpio\n"); diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 62a4a835acc6..696122c1b468 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -84,7 +84,7 @@ static int sdhci_sirf_probe(struct platform_device *pdev) * gets setup in sdhci_add_host() and we oops. */ if (gpio_is_valid(priv->gpio_cd)) { - ret = mmc_gpio_request_cd(host->mmc, priv->gpio_cd); + ret = mmc_gpio_request_cd(host->mmc, priv->gpio_cd, 0); if (ret) { dev_err(&pdev->dev, "card detect irq request failed: %d\n", ret); diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 6706b5e3b974..35f61cc3a392 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1436,7 +1436,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) } if (pd && pd->use_cd_gpio) { - ret = mmc_gpio_request_cd(mmc, pd->cd_gpio); + ret = mmc_gpio_request_cd(mmc, pd->cd_gpio, 0); if (ret < 0) goto erqcd; } diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 718843cfacfc..17f7fa99376e 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1110,7 +1110,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, dev_pm_qos_expose_latency_limit(&pdev->dev, 100); if (pdata->flags & TMIO_MMC_USE_GPIO_CD) { - ret = mmc_gpio_request_cd(mmc, pdata->cd_gpio); + ret = mmc_gpio_request_cd(mmc, pdata->cd_gpio, 0); if (ret < 0) { tmio_mmc_host_remove(_host); return ret; diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h index 7d88d27bfafa..b0c73e4cacea 100644 --- a/include/linux/mmc/slot-gpio.h +++ b/include/linux/mmc/slot-gpio.h @@ -18,7 +18,8 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio); void mmc_gpio_free_ro(struct mmc_host *host); int mmc_gpio_get_cd(struct mmc_host *host); -int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio); +int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, + unsigned int debounce); void mmc_gpio_free_cd(struct mmc_host *host); #endif -- cgit v1.2.3-71-gd317 From bf287a90ceedf86e3313ba0dcff606ac5399b39a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 8 Aug 2013 12:38:32 +0200 Subject: mmc: mmc_spi: Support CD/RO GPIOs Add support for passing CD/RO GPIO numbers directly to the mmc_spi driver instead of relying solely on board code callbacks to retrieve the CD/RO signals values. The driver will enable debouncing on the card detect GPIO if the cd_debounce field is set to a non-zero value. Signed-off-by: Laurent Pinchart Reviewed-by: H Hartley Sweeten Signed-off-by: Chris Ball --- drivers/mmc/host/mmc_spi.c | 33 ++++++++++++++++++++++--------- drivers/mmc/host/of_mmc_spi.c | 46 +++++++++++-------------------------------- include/linux/spi/mmc_spi.h | 16 +++++++++++++++ 3 files changed, 52 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 74145d1d51f5..62fb82d7c942 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -36,6 +36,7 @@ #include #include /* for R1_SPI_* bit values */ +#include #include #include @@ -1278,11 +1279,8 @@ static int mmc_spi_get_ro(struct mmc_host *mmc) if (host->pdata && host->pdata->get_ro) return !!host->pdata->get_ro(mmc->parent); - /* - * Board doesn't support read only detection; let the mmc core - * decide what to do. - */ - return -ENOSYS; + else + return mmc_gpio_get_ro(mmc); } static int mmc_spi_get_cd(struct mmc_host *mmc) @@ -1291,7 +1289,8 @@ static int mmc_spi_get_cd(struct mmc_host *mmc) if (host->pdata && host->pdata->get_cd) return !!host->pdata->get_cd(mmc->parent); - return -ENOSYS; + else + return mmc_gpio_get_cd(mmc); } static const struct mmc_host_ops mmc_spi_ops = { @@ -1324,6 +1323,7 @@ static int mmc_spi_probe(struct spi_device *spi) struct mmc_host *mmc; struct mmc_spi_host *host; int status; + bool has_ro = false; /* We rely on full duplex transfers, mostly to reduce * per-transfer overheads (by making fewer transfers). @@ -1448,18 +1448,33 @@ static int mmc_spi_probe(struct spi_device *spi) } /* pass platform capabilities, if any */ - if (host->pdata) + if (host->pdata) { mmc->caps |= host->pdata->caps; + mmc->caps2 |= host->pdata->caps2; + } status = mmc_add_host(mmc); if (status != 0) goto fail_add_host; + if (host->pdata && host->pdata->flags & MMC_SPI_USE_CD_GPIO) { + status = mmc_gpio_request_cd(mmc, host->pdata->cd_gpio, + host->pdata->cd_debounce); + if (status != 0) + goto fail_add_host; + } + + if (host->pdata && host->pdata->flags & MMC_SPI_USE_RO_GPIO) { + has_ro = true; + status = mmc_gpio_request_ro(mmc, host->pdata->ro_gpio); + if (status != 0) + goto fail_add_host; + } + dev_info(&spi->dev, "SD/MMC host %s%s%s%s%s\n", dev_name(&mmc->class_dev), host->dma_dev ? "" : ", no DMA", - (host->pdata && host->pdata->get_ro) - ? "" : ", no WP", + has_ro ? "" : ", no WP", (host->pdata && host->pdata->setpower) ? "" : ", no poweroff", (mmc->caps & MMC_CAP_NEEDS_POLL) diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c index d720b5e05b9c..6e218fb1a669 100644 --- a/drivers/mmc/host/of_mmc_spi.c +++ b/drivers/mmc/host/of_mmc_spi.c @@ -50,25 +50,6 @@ static struct of_mmc_spi *to_of_mmc_spi(struct device *dev) return container_of(dev->platform_data, struct of_mmc_spi, pdata); } -static int of_mmc_spi_read_gpio(struct device *dev, int gpio_num) -{ - struct of_mmc_spi *oms = to_of_mmc_spi(dev); - bool active_low = oms->alow_gpios[gpio_num]; - bool value = gpio_get_value(oms->gpios[gpio_num]); - - return active_low ^ value; -} - -static int of_mmc_spi_get_cd(struct device *dev) -{ - return of_mmc_spi_read_gpio(dev, CD_GPIO); -} - -static int of_mmc_spi_get_ro(struct device *dev) -{ - return of_mmc_spi_read_gpio(dev, WP_GPIO); -} - static int of_mmc_spi_init(struct device *dev, irqreturn_t (*irqhandler)(int, void *), void *mmc) { @@ -130,20 +111,22 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi) if (!gpio_is_valid(oms->gpios[i])) continue; - ret = gpio_request(oms->gpios[i], dev_name(dev)); - if (ret < 0) { - oms->gpios[i] = -EINVAL; - continue; - } - if (gpio_flags & OF_GPIO_ACTIVE_LOW) oms->alow_gpios[i] = true; } - if (gpio_is_valid(oms->gpios[CD_GPIO])) - oms->pdata.get_cd = of_mmc_spi_get_cd; - if (gpio_is_valid(oms->gpios[WP_GPIO])) - oms->pdata.get_ro = of_mmc_spi_get_ro; + if (gpio_is_valid(oms->gpios[CD_GPIO])) { + oms->pdata.cd_gpio = oms->gpios[CD_GPIO]; + oms->pdata.flags |= MMC_SPI_USE_CD_GPIO; + if (!oms->alow_gpios[CD_GPIO]) + oms->pdata.caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; + } + if (gpio_is_valid(oms->gpios[WP_GPIO])) { + oms->pdata.ro_gpio = oms->gpios[WP_GPIO]; + oms->pdata.flags |= MMC_SPI_USE_RO_GPIO; + if (!oms->alow_gpios[WP_GPIO]) + oms->pdata.caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; + } oms->detect_irq = irq_of_parse_and_map(np, 0); if (oms->detect_irq != 0) { @@ -166,15 +149,10 @@ void mmc_spi_put_pdata(struct spi_device *spi) struct device *dev = &spi->dev; struct device_node *np = dev->of_node; struct of_mmc_spi *oms = to_of_mmc_spi(dev); - int i; if (!dev->platform_data || !np) return; - for (i = 0; i < ARRAY_SIZE(oms->gpios); i++) { - if (gpio_is_valid(oms->gpios[i])) - gpio_free(oms->gpios[i]); - } kfree(oms); dev->platform_data = NULL; } diff --git a/include/linux/spi/mmc_spi.h b/include/linux/spi/mmc_spi.h index 32be8dbdf191..87cdb35bedda 100644 --- a/include/linux/spi/mmc_spi.h +++ b/include/linux/spi/mmc_spi.h @@ -7,6 +7,11 @@ struct device; struct mmc_host; +#define MMC_SPI_USE_CD_GPIO (1 << 0) +#define MMC_SPI_USE_RO_GPIO (1 << 1) +#define MMC_SPI_CD_GPIO_ACTIVE_LOW (1 << 2) +#define MMC_SPI_RO_GPIO_ACTIVE_LOW (1 << 3) + /* Put this in platform_data of a device being used to manage an MMC/SD * card slot. (Modeled after PXA mmc glue; see that for usage examples.) * @@ -30,8 +35,19 @@ struct mmc_spi_platform_data { */ int (*get_cd)(struct device *); + /* + * Card Detect and Read Only GPIOs. To enable debouncing on the card + * detect GPIO, set the cd_debounce to the debounce time in + * microseconds. + */ + unsigned int flags; + unsigned int cd_gpio; + unsigned int cd_debounce; + unsigned int ro_gpio; + /* Capabilities to pass into mmc core (e.g. MMC_CAP_NEEDS_POLL). */ unsigned long caps; + unsigned long caps2; /* how long to debounce card detect, in msecs */ u16 detect_delay; -- cgit v1.2.3-71-gd317 From 62b6af5c7ee28a3faf9638e509fd2da85a97229d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 8 Aug 2013 12:38:38 +0200 Subject: mmc: mmc_spi: Remove platform data .get_cd() and .get_ro() callbacks All platforms now pass the CD and RO GPIOs to the MMC SPI driver, those callbacks are not used anymore. Remove them. Signed-off-by: Laurent Pinchart Reviewed-by: H Hartley Sweeten Signed-off-by: Chris Ball --- drivers/mmc/host/mmc_spi.c | 24 ++---------------------- include/linux/spi/mmc_spi.h | 9 --------- 2 files changed, 2 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 62fb82d7c942..0a87e5691341 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1273,31 +1273,11 @@ static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } } -static int mmc_spi_get_ro(struct mmc_host *mmc) -{ - struct mmc_spi_host *host = mmc_priv(mmc); - - if (host->pdata && host->pdata->get_ro) - return !!host->pdata->get_ro(mmc->parent); - else - return mmc_gpio_get_ro(mmc); -} - -static int mmc_spi_get_cd(struct mmc_host *mmc) -{ - struct mmc_spi_host *host = mmc_priv(mmc); - - if (host->pdata && host->pdata->get_cd) - return !!host->pdata->get_cd(mmc->parent); - else - return mmc_gpio_get_cd(mmc); -} - static const struct mmc_host_ops mmc_spi_ops = { .request = mmc_spi_request, .set_ios = mmc_spi_set_ios, - .get_ro = mmc_spi_get_ro, - .get_cd = mmc_spi_get_cd, + .get_ro = mmc_gpio_get_ro, + .get_cd = mmc_gpio_get_cd, }; diff --git a/include/linux/spi/mmc_spi.h b/include/linux/spi/mmc_spi.h index 87cdb35bedda..274bc0fa00af 100644 --- a/include/linux/spi/mmc_spi.h +++ b/include/linux/spi/mmc_spi.h @@ -26,15 +26,6 @@ struct mmc_spi_platform_data { void *); void (*exit)(struct device *, void *); - /* sense switch on sd cards */ - int (*get_ro)(struct device *); - - /* - * If board does not use CD interrupts, driver can optimize polling - * using this function. - */ - int (*get_cd)(struct device *); - /* * Card Detect and Read Only GPIOs. To enable debouncing on the card * detect GPIO, set the cd_debounce to the debounce time in -- cgit v1.2.3-71-gd317 From dcbfaf36c1933d88565501ff13feb0f4b2d38735 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 8 Aug 2013 12:38:39 +0200 Subject: mmc: sh_mmcif: Remove .down_pwr() callback from platform data The callback isn't used by the driver and isn't initialized by board code. Remove it. Signed-off-by: Laurent Pinchart Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- include/linux/mmc/sh_mmcif.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h index e7d5dd67bb74..39011eb42930 100644 --- a/include/linux/mmc/sh_mmcif.h +++ b/include/linux/mmc/sh_mmcif.h @@ -34,7 +34,6 @@ struct sh_mmcif_plat_data { void (*set_pwr)(struct platform_device *pdev, int state); - void (*down_pwr)(struct platform_device *pdev); int (*get_cd)(struct platform_device *pdef); unsigned int slave_id_tx; /* embedded slave_id_[tr]x */ unsigned int slave_id_rx; -- cgit v1.2.3-71-gd317 From 83a0c7797e96e103bb3b6fcf8afb7b65dc7fc68e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 8 Aug 2013 12:38:40 +0200 Subject: mmc: sh_mmcif: Remove .set_pwr() callback from platform data The .set_pwr() callback isn't used anymore as all platforms register GPIO-controlled regulators. Remove it. Signed-off-by: Laurent Pinchart Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 3 --- include/linux/mmc/sh_mmcif.h | 1 - 2 files changed, 4 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 35f61cc3a392..ac8e109ac843 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -956,11 +956,8 @@ static int sh_mmcif_clk_update(struct sh_mmcif_host *host) static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios) { - struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data; struct mmc_host *mmc = host->mmc; - if (pd && pd->set_pwr) - pd->set_pwr(host->pd, ios->power_mode != MMC_POWER_OFF); if (!IS_ERR(mmc->supply.vmmc)) /* Errors ignored... */ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h index 39011eb42930..767fac5a7d93 100644 --- a/include/linux/mmc/sh_mmcif.h +++ b/include/linux/mmc/sh_mmcif.h @@ -33,7 +33,6 @@ */ struct sh_mmcif_plat_data { - void (*set_pwr)(struct platform_device *pdev, int state); int (*get_cd)(struct platform_device *pdef); unsigned int slave_id_tx; /* embedded slave_id_[tr]x */ unsigned int slave_id_rx; -- cgit v1.2.3-71-gd317 From 57fcb523e5fce1c24e9c28b64f6e6dc3edf55073 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 8 Aug 2013 12:38:41 +0200 Subject: mmc: sh_mobile_sdhi: Remove .get_cd() callback from platform data All platforms pass the CD GPIO number to the driver in the .cd_gpio field. The .get_cd() callback isn't used anymore, remove it. Signed-off-by: Laurent Pinchart Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 9 --------- include/linux/mmc/sh_mobile_sdhi.h | 1 - 2 files changed, 10 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index ebea749297c2..663a67271670 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -77,13 +77,6 @@ static void sh_mobile_sdhi_set_pwr(struct platform_device *pdev, int state) p->set_pwr(pdev, state); } -static int sh_mobile_sdhi_get_cd(struct platform_device *pdev) -{ - struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; - - return p->get_cd(pdev); -} - static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host) { int timeout = 1000; @@ -182,8 +175,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->cd_gpio = p->cd_gpio; if (p->set_pwr) mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; - if (p->get_cd) - mmc_data->get_cd = sh_mobile_sdhi_get_cd; if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { /* diff --git a/include/linux/mmc/sh_mobile_sdhi.h b/include/linux/mmc/sh_mobile_sdhi.h index b76bcf0621f6..f31c69262232 100644 --- a/include/linux/mmc/sh_mobile_sdhi.h +++ b/include/linux/mmc/sh_mobile_sdhi.h @@ -26,7 +26,6 @@ struct sh_mobile_sdhi_info { u32 tmio_ocr_mask; /* available MMC voltages */ unsigned int cd_gpio; void (*set_pwr)(struct platform_device *pdev, int state); - int (*get_cd)(struct platform_device *pdev); /* callbacks for board specific setup code */ int (*init)(struct platform_device *pdev, -- cgit v1.2.3-71-gd317 From 1036563e1417c050993bbd48f20a84ff7c7cef99 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 8 Aug 2013 12:38:42 +0200 Subject: mmc: sh_mobile_sdhi: Remove .set_pwr() callback from platform data The .set_pwr() callback isn't used anymore as all platforms register GPIO-controlled regulators. Remove it. Signed-off-by: Laurent Pinchart Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 9 --------- include/linux/mmc/sh_mobile_sdhi.h | 1 - 2 files changed, 10 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 663a67271670..b3fd2c366678 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -70,13 +70,6 @@ static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev) clk_disable(priv->clk); } -static void sh_mobile_sdhi_set_pwr(struct platform_device *pdev, int state) -{ - struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; - - p->set_pwr(pdev, state); -} - static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host) { int timeout = 1000; @@ -173,8 +166,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->capabilities |= p->tmio_caps; mmc_data->capabilities2 |= p->tmio_caps2; mmc_data->cd_gpio = p->cd_gpio; - if (p->set_pwr) - mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { /* diff --git a/include/linux/mmc/sh_mobile_sdhi.h b/include/linux/mmc/sh_mobile_sdhi.h index f31c69262232..68927ae50845 100644 --- a/include/linux/mmc/sh_mobile_sdhi.h +++ b/include/linux/mmc/sh_mobile_sdhi.h @@ -25,7 +25,6 @@ struct sh_mobile_sdhi_info { unsigned long tmio_caps2; u32 tmio_ocr_mask; /* available MMC voltages */ unsigned int cd_gpio; - void (*set_pwr)(struct platform_device *pdev, int state); /* callbacks for board specific setup code */ int (*init)(struct platform_device *pdev, -- cgit v1.2.3-71-gd317 From 2b63b341d42cd64ff40062447320d46cf3e7f0bb Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 8 Aug 2013 12:38:43 +0200 Subject: mmc: tmio-mmc: Remove .get_cd() callback from platform data All platforms pass the CD GPIO number to the driver in the .cd_gpio field. The .get_cd() callback isn't used anymore, remove it Signed-off-by: Laurent Pinchart Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 16 +--------------- include/linux/mfd/tmio.h | 1 - 2 files changed, 1 insertion(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 17f7fa99376e..b3802256f954 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -936,25 +936,11 @@ static int tmio_mmc_get_ro(struct mmc_host *mmc) (sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT)); } -static int tmio_mmc_get_cd(struct mmc_host *mmc) -{ - struct tmio_mmc_host *host = mmc_priv(mmc); - struct tmio_mmc_data *pdata = host->pdata; - int ret = mmc_gpio_get_cd(mmc); - if (ret >= 0) - return ret; - - if (!pdata->get_cd) - return -ENOSYS; - else - return pdata->get_cd(host->pdev); -} - static const struct mmc_host_ops tmio_mmc_ops = { .request = tmio_mmc_request, .set_ios = tmio_mmc_set_ios, .get_ro = tmio_mmc_get_ro, - .get_cd = tmio_mmc_get_cd, + .get_cd = mmc_gpio_get_cd, .enable_sdio_irq = tmio_mmc_enable_sdio_irq, }; diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index ce3511326f80..b22883d60500 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -108,7 +108,6 @@ struct tmio_mmc_data { unsigned int cd_gpio; void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); - int (*get_cd)(struct platform_device *host); int (*write16_hook)(struct tmio_mmc_host *host, int addr); /* clock management callbacks */ int (*clk_enable)(struct platform_device *pdev, unsigned int *f); -- cgit v1.2.3-71-gd317 From 3af9d15c719017feb63fa99f89ac6009a5a3d467 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 8 Aug 2013 12:38:44 +0200 Subject: mmc: tmio-mmc: Remove .set_pwr() callback from platform data The .set_pwr() callback isn't used anymore as all platforms register GPIO-controlled regulators. Remove it. Signed-off-by: Laurent Pinchart Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc.h | 1 - drivers/mmc/host/tmio_mmc_pio.c | 7 ------- include/linux/mfd/tmio.h | 1 - 3 files changed, 9 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 86fd21e00099..39c2f45102c8 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -68,7 +68,6 @@ struct tmio_mmc_host { enum tmio_mmc_power power; /* Callbacks for clock / power control */ - void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); /* pio related stuff */ diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index b3802256f954..67a3bf1238cf 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -777,9 +777,6 @@ static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd) /* .set_ios() is returning void, so, no chance to report an error */ - if (host->set_pwr) - host->set_pwr(host->pdev, 1); - if (!IS_ERR(mmc->supply.vmmc)) { ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); /* @@ -813,9 +810,6 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host) if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); - - if (host->set_pwr) - host->set_pwr(host->pdev, 0); } /* Set MMC clock / power. @@ -1002,7 +996,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, _host->pdev = pdev; platform_set_drvdata(pdev, mmc); - _host->set_pwr = pdata->set_pwr; _host->set_clk_div = pdata->set_clk_div; /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index b22883d60500..baa23464a1e6 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -106,7 +106,6 @@ struct tmio_mmc_data { struct tmio_mmc_dma *dma; struct device *dev; unsigned int cd_gpio; - void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); int (*write16_hook)(struct tmio_mmc_host *host, int addr); /* clock management callbacks */ -- cgit v1.2.3-71-gd317 From d00cadacbe47d4883b0d5e38aa73a3f4e171d37e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 2 Aug 2013 14:48:02 +0200 Subject: mmc: sh_mmcif: move header include from header into .c sh_dma.h isn't needed in sh_mmcif.h, move it into sh_mmcif.c. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 1 + include/linux/mmc/sh_mmcif.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index ac8e109ac843..2596664246c1 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -61,6 +61,7 @@ #include #include #include +#include #include #include diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h index 767fac5a7d93..c4880ffb6cce 100644 --- a/include/linux/mmc/sh_mmcif.h +++ b/include/linux/mmc/sh_mmcif.h @@ -16,7 +16,6 @@ #include #include -#include /* * MMCIF : CE_CLK_CTRL [19:16] -- cgit v1.2.3-71-gd317 From 967bcb77177cda1a426fdb2350e6ec61bcf5b5eb Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 10 Jul 2013 21:21:12 +0200 Subject: mmc: sh_mmcif: revision-specific Command Completion Signal handling Some earlier MMCIF IP revisions contained Command Completion Signal support, which has been dropped again in modern versions. Sopport for this feature is added in a way to preserve the current behaviour by default, i.e. when it is not enabled in platform data. Patch is based on work by Nobuyuki HIRAI. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 27 +++++++++++++++++++++------ include/linux/mmc/sh_mmcif.h | 1 + 2 files changed, 22 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 2acfa41d609f..6fa6868d1030 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -134,6 +134,8 @@ INT_BUFWEN | INT_CMD12DRE | INT_BUFRE | \ INT_DTRANE | INT_CMD12RBE | INT_CMD12CRE) +#define INT_CCS (INT_CCSTO | INT_CCSRCV | INT_CCSDE) + /* CE_INT_MASK */ #define MASK_ALL 0x00000000 #define MASK_MCCSDE (1 << 29) @@ -162,7 +164,7 @@ #define MASK_START_CMD (MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR | \ MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR | \ - MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | \ + MASK_MCRCSTO | MASK_MWDATTO | \ MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO) #define MASK_CLEAN (INT_ERR_STS | MASK_MRBSYE | MASK_MCRSPE | \ @@ -244,6 +246,7 @@ struct sh_mmcif_host { int sg_blkidx; bool power; bool card_present; + bool ccs_enable; /* Command Completion Signal support */ struct mutex thread_lock; /* DMA support */ @@ -492,8 +495,10 @@ static void sh_mmcif_sync_reset(struct sh_mmcif_host *host) sh_mmcif_writel(host->addr, MMCIF_CE_VERSION, SOFT_RST_ON); sh_mmcif_writel(host->addr, MMCIF_CE_VERSION, SOFT_RST_OFF); + if (host->ccs_enable) + tmp |= SCCSTO_29; sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, tmp | - SRSPTO_256 | SRBSYTO_29 | SRWDTO_29 | SCCSTO_29); + SRSPTO_256 | SRBSYTO_29 | SRWDTO_29); /* byte swap on */ sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_ATYP); } @@ -873,6 +878,9 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, break; } + if (host->ccs_enable) + mask |= MASK_MCCSTO; + if (mrq->data) { sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, 0); sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, @@ -880,7 +888,10 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, } opc = sh_mmcif_set_cmd(host, mrq); - sh_mmcif_writel(host->addr, MMCIF_CE_INT, 0xD80430C0); + if (host->ccs_enable) + sh_mmcif_writel(host->addr, MMCIF_CE_INT, 0xD80430C0); + else + sh_mmcif_writel(host->addr, MMCIF_CE_INT, 0xD80430C0 | INT_CCS); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, mask); /* set arg */ sh_mmcif_writel(host->addr, MMCIF_CE_ARG, cmd->arg); @@ -1245,11 +1256,14 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; - u32 state; + u32 state, mask; state = sh_mmcif_readl(host->addr, MMCIF_CE_INT); - sh_mmcif_writel(host->addr, MMCIF_CE_INT, - ~(state & sh_mmcif_readl(host->addr, MMCIF_CE_INT_MASK))); + mask = sh_mmcif_readl(host->addr, MMCIF_CE_INT_MASK); + if (host->ccs_enable) + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(state & mask)); + else + sh_mmcif_writel(host->addr, MMCIF_CE_INT, INT_CCS | ~(state & mask)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN); if (state & ~MASK_CLEAN) @@ -1383,6 +1397,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) host->mmc = mmc; host->addr = reg; host->timeout = msecs_to_jiffies(1000); + host->ccs_enable = !pd || !pd->ccs_unsupported; host->pd = pdev; diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h index c4880ffb6cce..197ed91f6a0c 100644 --- a/include/linux/mmc/sh_mmcif.h +++ b/include/linux/mmc/sh_mmcif.h @@ -36,6 +36,7 @@ struct sh_mmcif_plat_data { unsigned int slave_id_tx; /* embedded slave_id_[tr]x */ unsigned int slave_id_rx; bool use_cd_gpio : 1; + bool ccs_unsupported : 1; unsigned int cd_gpio; u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */ unsigned long caps; -- cgit v1.2.3-71-gd317 From 6d6fd3674259d16b735c961743ff28870c46cedc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 10 Jul 2013 21:21:13 +0200 Subject: mmc: sh_mmcif: revision-specific CLK_CTRL2 handling Some newer MMCIF IP revisions contain a CE_CLK_CTRL2 register, that has to be set for proper operation. Support for this feature is added in a way to preserve the current behaviour by default, i.e. when it is not enabled in platform data. Patch is based on work by Nobuyuki HIRAI. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 4 ++++ include/linux/mmc/sh_mmcif.h | 2 ++ 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 6fa6868d1030..36629a024aa1 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -247,6 +247,7 @@ struct sh_mmcif_host { bool power; bool card_present; bool ccs_enable; /* Command Completion Signal support */ + bool clk_ctrl2_enable; struct mutex thread_lock; /* DMA support */ @@ -497,6 +498,8 @@ static void sh_mmcif_sync_reset(struct sh_mmcif_host *host) sh_mmcif_writel(host->addr, MMCIF_CE_VERSION, SOFT_RST_OFF); if (host->ccs_enable) tmp |= SCCSTO_29; + if (host->clk_ctrl2_enable) + sh_mmcif_writel(host->addr, MMCIF_CE_CLK_CTRL2, 0x0F0F0000); sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, tmp | SRSPTO_256 | SRBSYTO_29 | SRWDTO_29); /* byte swap on */ @@ -1398,6 +1401,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) host->addr = reg; host->timeout = msecs_to_jiffies(1000); host->ccs_enable = !pd || !pd->ccs_unsupported; + host->clk_ctrl2_enable = pd && pd->clk_ctrl2_present; host->pd = pdev; diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h index 197ed91f6a0c..ccd8fb2cad52 100644 --- a/include/linux/mmc/sh_mmcif.h +++ b/include/linux/mmc/sh_mmcif.h @@ -37,6 +37,7 @@ struct sh_mmcif_plat_data { unsigned int slave_id_rx; bool use_cd_gpio : 1; bool ccs_unsupported : 1; + bool clk_ctrl2_present : 1; unsigned int cd_gpio; u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */ unsigned long caps; @@ -60,6 +61,7 @@ struct sh_mmcif_plat_data { #define MMCIF_CE_INT_MASK 0x00000044 #define MMCIF_CE_HOST_STS1 0x00000048 #define MMCIF_CE_HOST_STS2 0x0000004C +#define MMCIF_CE_CLK_CTRL2 0x00000070 #define MMCIF_CE_VERSION 0x0000007C /* CE_BUF_ACC */ -- cgit v1.2.3-71-gd317 From 8be5ad9acb7c11b9d717bcdbe3d6de0dab081a39 Mon Sep 17 00:00:00 2001 From: Marc Carino Date: Sat, 24 Aug 2013 23:22:48 -0700 Subject: libata: Populate host-to-device FIS "auxiliary" field SATA 3.1 added an "auxiliary" field to the host-to-device FIS. Populate the host-to-device FIS with the new field via the taskfile struct. Signed-off-by: Marc Carino Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 10 ++++++---- include/linux/libata.h | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 39e78e4524a3..2b4e92568850 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -546,6 +546,8 @@ int atapi_cmd_type(u8 opcode) */ void ata_tf_to_fis(const struct ata_taskfile *tf, u8 pmp, int is_cmd, u8 *fis) { + const __le32 aux = cpu_to_le32(tf->auxiliary); + fis[0] = 0x27; /* Register - Host to Device FIS */ fis[1] = pmp & 0xf; /* Port multiplier number*/ if (is_cmd) @@ -569,10 +571,10 @@ void ata_tf_to_fis(const struct ata_taskfile *tf, u8 pmp, int is_cmd, u8 *fis) fis[14] = 0; fis[15] = tf->ctl; - fis[16] = 0; - fis[17] = 0; - fis[18] = 0; - fis[19] = 0; + fis[16] = aux & 0xff; + fis[17] = (aux >> 8) & 0xff; + fis[18] = (aux >> 16) & 0xff; + fis[19] = (aux >> 24) & 0xff; } /** diff --git a/include/linux/libata.h b/include/linux/libata.h index 3198617905e1..9b6ba196179f 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -555,6 +555,10 @@ struct ata_taskfile { u8 device; u8 command; /* IO operation */ + + u32 auxiliary; /* auxiliary field */ + /* from SATA 3.1 and */ + /* ATA-8 ACS-3 */ }; #ifdef CONFIG_ATA_SFF -- cgit v1.2.3-71-gd317 From 40fb59e75ad197a1c4ca2fbccef85432d8d103f8 Mon Sep 17 00:00:00 2001 From: Marc Carino Date: Sat, 24 Aug 2013 23:22:49 -0700 Subject: libata: Add H2D FIS "auxiliary" port flag Add a new port flag, ATA_FLAG_FPDMA_AUX, used to indicate support for transmission of the H2D FIS 'auxiliary' field. Signed-off-by: Marc Carino Signed-off-by: Tejun Heo --- drivers/ata/ahci.c | 8 ++++++++ include/linux/libata.h | 1 + 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 5064f3ea20f1..8d41c57fd969 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1288,6 +1288,14 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) */ if (!(hpriv->flags & AHCI_HFLAG_NO_FPDMA_AA)) pi.flags |= ATA_FLAG_FPDMA_AA; + + /* + * All AHCI controllers should be forward-compatible + * with the new auxiliary field. This code should be + * conditionalized if any buggy AHCI controllers are + * encountered. + */ + pi.flags |= ATA_FLAG_FPDMA_AUX; } if (hpriv->cap & HOST_CAP_PMP) diff --git a/include/linux/libata.h b/include/linux/libata.h index 9b6ba196179f..578f98c3ff57 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -223,6 +223,7 @@ enum { ATA_FLAG_ACPI_SATA = (1 << 17), /* need native SATA ACPI layout */ ATA_FLAG_AN = (1 << 18), /* controller supports AN */ ATA_FLAG_PMP = (1 << 19), /* controller supports PMP */ + ATA_FLAG_FPDMA_AUX = (1 << 20), /* controller supports H2DFIS aux field */ ATA_FLAG_EM = (1 << 21), /* driver supports enclosure * management */ ATA_FLAG_SW_ACTIVITY = (1 << 22), /* driver supports sw activity -- cgit v1.2.3-71-gd317 From ed36911c747c18525691c0aa0fbe6b918c8eac19 Mon Sep 17 00:00:00 2001 From: Marc Carino Date: Sat, 24 Aug 2013 23:22:50 -0700 Subject: libata: Add support for SEND/RECEIVE FPDMA QUEUED Add support for the following ATA opcodes, which are present in SATA 3.1 and T13 ATA ACS-3: SEND FPDMA QUEUED RECEIVE FPDMA QUEUED Signed-off-by: Marc Carino Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 16 ++++++++++++++++ include/linux/ata.h | 21 +++++++++++++++++++++ include/linux/libata.h | 4 ++++ 3 files changed, 41 insertions(+) (limited to 'include') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 2b4e92568850..a044be99841a 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2141,6 +2141,22 @@ static int ata_dev_config_ncq(struct ata_device *dev, else snprintf(desc, desc_sz, "NCQ (depth %d/%d)%s", hdepth, ddepth, aa_desc); + + if ((ap->flags & ATA_FLAG_FPDMA_AUX) && + ata_id_has_ncq_send_and_recv(dev->id)) { + err_mask = ata_read_log_page(dev, ATA_LOG_NCQ_SEND_RECV, + 0, ap->sector_buf, 1); + if (err_mask) { + ata_dev_dbg(dev, + "failed to get NCQ Send/Recv Log Emask 0x%x\n", + err_mask); + } else { + dev->flags |= ATA_DFLAG_NCQ_SEND_RECV; + memcpy(dev->ncq_send_recv_cmds, ap->sector_buf, + ATA_LOG_NCQ_SEND_RECV_SIZE); + } + } + return 0; } diff --git a/include/linux/ata.h b/include/linux/ata.h index f63fb1afc5cc..bf4c69ca76df 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -239,6 +239,8 @@ enum { ATA_CMD_WRITE_QUEUED_FUA_EXT = 0x3E, ATA_CMD_FPDMA_READ = 0x60, ATA_CMD_FPDMA_WRITE = 0x61, + ATA_CMD_FPDMA_SEND = 0x64, + ATA_CMD_FPDMA_RECV = 0x65, ATA_CMD_PIO_READ = 0x20, ATA_CMD_PIO_READ_EXT = 0x24, ATA_CMD_PIO_WRITE = 0x30, @@ -293,8 +295,13 @@ enum { /* marked obsolete in the ATA/ATAPI-7 spec */ ATA_CMD_RESTORE = 0x10, + /* Subcmds for ATA_CMD_FPDMA_SEND */ + ATA_SUBCMD_FPDMA_SEND_DSM = 0x00, + ATA_SUBCMD_FPDMA_SEND_WR_LOG_DMA_EXT = 0x02, + /* READ_LOG_EXT pages */ ATA_LOG_SATA_NCQ = 0x10, + ATA_LOG_NCQ_SEND_RECV = 0x13, ATA_LOG_SATA_ID_DEV_DATA = 0x30, ATA_LOG_SATA_SETTINGS = 0x08, ATA_LOG_DEVSLP_OFFSET = 0x30, @@ -305,6 +312,15 @@ enum { ATA_LOG_DEVSLP_VALID = 0x07, ATA_LOG_DEVSLP_VALID_MASK = 0x80, + /* NCQ send and receive log */ + ATA_LOG_NCQ_SEND_RECV_SUBCMDS_OFFSET = 0x00, + ATA_LOG_NCQ_SEND_RECV_SUBCMDS_DSM = (1 << 0), + ATA_LOG_NCQ_SEND_RECV_DSM_OFFSET = 0x04, + ATA_LOG_NCQ_SEND_RECV_DSM_TRIM = (1 << 0), + ATA_LOG_NCQ_SEND_RECV_RD_LOG_OFFSET = 0x08, + ATA_LOG_NCQ_SEND_RECV_WR_LOG_OFFSET = 0x0C, + ATA_LOG_NCQ_SEND_RECV_SIZE = 0x10, + /* READ/WRITE LONG (obsolete) */ ATA_CMD_READ_LONG = 0x22, ATA_CMD_READ_LONG_ONCE = 0x23, @@ -772,6 +788,11 @@ static inline int ata_id_rotation_rate(const u16 *id) return val; } +static inline bool ata_id_has_ncq_send_and_recv(const u16 *id) +{ + return id[ATA_ID_SATA_CAPABILITY_2] & BIT(6); +} + static inline bool ata_id_has_trim(const u16 *id) { if (ata_id_major_version(id) >= 7 && diff --git a/include/linux/libata.h b/include/linux/libata.h index 578f98c3ff57..a988c2d7d0e8 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -172,6 +172,7 @@ enum { ATA_DFLAG_DUBIOUS_XFER = (1 << 16), /* data transfer not verified */ ATA_DFLAG_NO_UNLOAD = (1 << 17), /* device doesn't support unload */ ATA_DFLAG_UNLOCK_HPA = (1 << 18), /* unlock HPA */ + ATA_DFLAG_NCQ_SEND_RECV = (1 << 19), /* device supports NCQ SEND and RECV */ ATA_DFLAG_INIT_MASK = (1 << 24) - 1, ATA_DFLAG_DETACH = (1 << 24), @@ -704,6 +705,9 @@ struct ata_device { /* DEVSLP Timing Variables from Identify Device Data Log */ u8 devslp_timing[ATA_LOG_DEVSLP_SIZE]; + /* NCQ send and receive log subcommand support */ + u8 ncq_send_recv_cmds[ATA_LOG_NCQ_SEND_RECV_SIZE]; + /* error history */ int spdn_cnt; /* ering is CLEAR_END, read comment above CLEAR_END */ -- cgit v1.2.3-71-gd317 From 87fb6c31b9ef9ccab9a822b01fc3911f026a29bb Mon Sep 17 00:00:00 2001 From: Marc Carino Date: Sat, 24 Aug 2013 23:22:51 -0700 Subject: libata: Add support for queued DSM TRIM Some new SSDs support the queued version of the DSM TRIM command. Let the driver use the new command if supported. Signed-off-by: Marc Carino Signed-off-by: Tejun Heo --- drivers/ata/libata-scsi.c | 25 +++++++++++++++++++------ include/linux/libata.h | 7 +++++++ 2 files changed, 26 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index f177ad6bfd09..58026106fcd9 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3097,12 +3097,25 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc) buf = page_address(sg_page(scsi_sglist(scmd))); size = ata_set_lba_range_entries(buf, 512, block, n_block); - tf->protocol = ATA_PROT_DMA; - tf->hob_feature = 0; - tf->feature = ATA_DSM_TRIM; - tf->hob_nsect = (size / 512) >> 8; - tf->nsect = size / 512; - tf->command = ATA_CMD_DSM; + if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) { + /* Newer devices support queued TRIM commands */ + tf->protocol = ATA_PROT_NCQ; + tf->command = ATA_CMD_FPDMA_SEND; + tf->hob_nsect = ATA_SUBCMD_FPDMA_SEND_DSM & 0x1f; + tf->nsect = qc->tag << 3; + tf->hob_feature = (size / 512) >> 8; + tf->feature = size / 512; + + tf->auxiliary = 1; + } else { + tf->protocol = ATA_PROT_DMA; + tf->hob_feature = 0; + tf->feature = ATA_DSM_TRIM; + tf->hob_nsect = (size / 512) >> 8; + tf->nsect = size / 512; + tf->command = ATA_CMD_DSM; + } + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 | ATA_TFLAG_WRITE; diff --git a/include/linux/libata.h b/include/linux/libata.h index a988c2d7d0e8..0e23c26485f4 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1606,6 +1606,13 @@ static inline int ata_ncq_enabled(struct ata_device *dev) ATA_DFLAG_NCQ)) == ATA_DFLAG_NCQ; } +static inline bool ata_fpdma_dsm_supported(struct ata_device *dev) +{ + return (dev->flags & ATA_DFLAG_NCQ_SEND_RECV) && + (dev->ncq_send_recv_cmds[ATA_LOG_NCQ_SEND_RECV_DSM_OFFSET] & + ATA_LOG_NCQ_SEND_RECV_DSM_TRIM); +} + static inline void ata_qc_set_polling(struct ata_queued_cmd *qc) { qc->tf.ctl |= ATA_NIEN; -- cgit v1.2.3-71-gd317 From 4b0a8670852c1f577014bf2dc4f8a9d1fd908bb9 Mon Sep 17 00:00:00 2001 From: Raghavendra K T Date: Mon, 26 Aug 2013 14:18:33 +0530 Subject: kvm uapi: Add KICK_CPU and PV_UNHALT definition to uapi this is needed by both guest and host. Originally-from: Srivatsa Vaddagiri Signed-off-by: Raghavendra K T Acked-by: Gleb Natapov Acked-by: Ingo Molnar Signed-off-by: Gleb Natapov --- arch/x86/include/uapi/asm/kvm_para.h | 1 + include/uapi/linux/kvm_para.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include') diff --git a/arch/x86/include/uapi/asm/kvm_para.h b/arch/x86/include/uapi/asm/kvm_para.h index 06fdbd987e97..94dc8ca434e0 100644 --- a/arch/x86/include/uapi/asm/kvm_para.h +++ b/arch/x86/include/uapi/asm/kvm_para.h @@ -23,6 +23,7 @@ #define KVM_FEATURE_ASYNC_PF 4 #define KVM_FEATURE_STEAL_TIME 5 #define KVM_FEATURE_PV_EOI 6 +#define KVM_FEATURE_PV_UNHALT 7 /* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h index cea2c5c72d26..2841f86eae0b 100644 --- a/include/uapi/linux/kvm_para.h +++ b/include/uapi/linux/kvm_para.h @@ -19,6 +19,7 @@ #define KVM_HC_MMU_OP 2 #define KVM_HC_FEATURES 3 #define KVM_HC_PPC_MAP_MAGIC_PAGE 4 +#define KVM_HC_KICK_CPU 5 /* * hypercalls use architecture specific -- cgit v1.2.3-71-gd317 From a89bcd4c6c2023615a89001b5a11b0bb77eb9491 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 22 Aug 2013 06:14:22 -0300 Subject: [media] adv7842: add new video decoder driver This is a Analog Devices Component/Graphics/SD Digitizer with 2:1 Multiplexed HDMI Receiver. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 12 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/adv7842.c | 2946 +++++++++++++++++++++++++++++++++++++++++++ include/media/adv7842.h | 226 ++++ 4 files changed, 3185 insertions(+) create mode 100644 drivers/media/i2c/adv7842.c create mode 100644 include/media/adv7842.h (limited to 'include') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index b2cd8ca51af7..847b7113f706 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -206,6 +206,18 @@ config VIDEO_ADV7604 To compile this driver as a module, choose M here: the module will be called adv7604. +config VIDEO_ADV7842 + tristate "Analog Devices ADV7842 decoder" + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + ---help--- + Support for the Analog Devices ADV7842 video decoder. + + This is a Analog Devices Component/Graphics/SD Digitizer + with 2:1 Multiplexed HDMI Receiver. + + To compile this driver as a module, choose M here: the + module will be called adv7842. + config VIDEO_BT819 tristate "BT819A VideoStream decoder" depends on VIDEO_V4L2 && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index dc20653bb5ad..b4cf972703d2 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o +obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_VS6624) += vs6624.o diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c new file mode 100644 index 000000000000..d1748901337c --- /dev/null +++ b/drivers/media/i2c/adv7842.c @@ -0,0 +1,2946 @@ +/* + * adv7842 - Analog Devices ADV7842 video decoder driver + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * References (c = chapter, p = page): + * REF_01 - Analog devices, ADV7842, Register Settings Recommendations, + * Revision 2.5, June 2010 + * REF_02 - Analog devices, Register map documentation, Documentation of + * the register maps, Software manual, Rev. F, June 2010 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-2)"); + +MODULE_DESCRIPTION("Analog Devices ADV7842 video decoder driver"); +MODULE_AUTHOR("Hans Verkuil "); +MODULE_AUTHOR("Martin Bugge "); +MODULE_LICENSE("GPL"); + +/* ADV7842 system clock frequency */ +#define ADV7842_fsc (28636360) + +/* +********************************************************************** +* +* Arrays with configuration parameters for the ADV7842 +* +********************************************************************** +*/ + +struct adv7842_state { + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler hdl; + enum adv7842_mode mode; + struct v4l2_dv_timings timings; + enum adv7842_vid_std_select vid_std_select; + v4l2_std_id norm; + struct { + u8 edid[256]; + u32 present; + } hdmi_edid; + struct { + u8 edid[256]; + u32 present; + } vga_edid; + struct v4l2_fract aspect_ratio; + u32 rgb_quantization_range; + bool is_cea_format; + struct workqueue_struct *work_queues; + struct delayed_work delayed_work_enable_hotplug; + bool connector_hdmi; + bool hdmi_port_a; + + /* i2c clients */ + struct i2c_client *i2c_sdp_io; + struct i2c_client *i2c_sdp; + struct i2c_client *i2c_cp; + struct i2c_client *i2c_vdp; + struct i2c_client *i2c_afe; + struct i2c_client *i2c_hdmi; + struct i2c_client *i2c_repeater; + struct i2c_client *i2c_edid; + struct i2c_client *i2c_infoframe; + struct i2c_client *i2c_cec; + struct i2c_client *i2c_avlink; + + /* controls */ + struct v4l2_ctrl *detect_tx_5v_ctrl; + struct v4l2_ctrl *analog_sampling_phase_ctrl; + struct v4l2_ctrl *free_run_color_ctrl_manual; + struct v4l2_ctrl *free_run_color_ctrl; + struct v4l2_ctrl *rgb_quantization_range_ctrl; +}; + +/* Unsupported timings. This device cannot support 720p30. */ +static const struct v4l2_dv_timings adv7842_timings_exceptions[] = { + V4L2_DV_BT_CEA_1280X720P30, + { } +}; + +static bool adv7842_check_dv_timings(const struct v4l2_dv_timings *t, void *hdl) +{ + int i; + + for (i = 0; adv7842_timings_exceptions[i].bt.width; i++) + if (v4l2_match_dv_timings(t, adv7842_timings_exceptions + i, 0)) + return false; + return true; +} + +struct adv7842_video_standards { + struct v4l2_dv_timings timings; + u8 vid_std; + u8 v_freq; +}; + +/* sorted by number of lines */ +static const struct adv7842_video_standards adv7842_prim_mode_comp[] = { + /* { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, TODO flickering */ + { V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 }, + { V4L2_DV_BT_CEA_1280X720P50, 0x19, 0x01 }, + { V4L2_DV_BT_CEA_1280X720P60, 0x19, 0x00 }, + { V4L2_DV_BT_CEA_1920X1080P24, 0x1e, 0x04 }, + { V4L2_DV_BT_CEA_1920X1080P25, 0x1e, 0x03 }, + { V4L2_DV_BT_CEA_1920X1080P30, 0x1e, 0x02 }, + { V4L2_DV_BT_CEA_1920X1080P50, 0x1e, 0x01 }, + { V4L2_DV_BT_CEA_1920X1080P60, 0x1e, 0x00 }, + /* TODO add 1920x1080P60_RB (CVT timing) */ + { }, +}; + +/* sorted by number of lines */ +static const struct adv7842_video_standards adv7842_prim_mode_gr[] = { + { V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 }, + { V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 }, + { V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 }, + { V4L2_DV_BT_DMT_640X480P85, 0x0b, 0x00 }, + { V4L2_DV_BT_DMT_800X600P56, 0x00, 0x00 }, + { V4L2_DV_BT_DMT_800X600P60, 0x01, 0x00 }, + { V4L2_DV_BT_DMT_800X600P72, 0x02, 0x00 }, + { V4L2_DV_BT_DMT_800X600P75, 0x03, 0x00 }, + { V4L2_DV_BT_DMT_800X600P85, 0x04, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P60, 0x0c, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P70, 0x0d, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P75, 0x0e, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P85, 0x0f, 0x00 }, + { V4L2_DV_BT_DMT_1280X1024P60, 0x05, 0x00 }, + { V4L2_DV_BT_DMT_1280X1024P75, 0x06, 0x00 }, + { V4L2_DV_BT_DMT_1360X768P60, 0x12, 0x00 }, + { V4L2_DV_BT_DMT_1366X768P60, 0x13, 0x00 }, + { V4L2_DV_BT_DMT_1400X1050P60, 0x14, 0x00 }, + { V4L2_DV_BT_DMT_1400X1050P75, 0x15, 0x00 }, + { V4L2_DV_BT_DMT_1600X1200P60, 0x16, 0x00 }, /* TODO not tested */ + /* TODO add 1600X1200P60_RB (not a DMT timing) */ + { V4L2_DV_BT_DMT_1680X1050P60, 0x18, 0x00 }, + { V4L2_DV_BT_DMT_1920X1200P60_RB, 0x19, 0x00 }, /* TODO not tested */ + { }, +}; + +/* sorted by number of lines */ +static const struct adv7842_video_standards adv7842_prim_mode_hdmi_comp[] = { + { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, + { V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 }, + { V4L2_DV_BT_CEA_1280X720P50, 0x13, 0x01 }, + { V4L2_DV_BT_CEA_1280X720P60, 0x13, 0x00 }, + { V4L2_DV_BT_CEA_1920X1080P24, 0x1e, 0x04 }, + { V4L2_DV_BT_CEA_1920X1080P25, 0x1e, 0x03 }, + { V4L2_DV_BT_CEA_1920X1080P30, 0x1e, 0x02 }, + { V4L2_DV_BT_CEA_1920X1080P50, 0x1e, 0x01 }, + { V4L2_DV_BT_CEA_1920X1080P60, 0x1e, 0x00 }, + { }, +}; + +/* sorted by number of lines */ +static const struct adv7842_video_standards adv7842_prim_mode_hdmi_gr[] = { + { V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 }, + { V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 }, + { V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 }, + { V4L2_DV_BT_DMT_640X480P85, 0x0b, 0x00 }, + { V4L2_DV_BT_DMT_800X600P56, 0x00, 0x00 }, + { V4L2_DV_BT_DMT_800X600P60, 0x01, 0x00 }, + { V4L2_DV_BT_DMT_800X600P72, 0x02, 0x00 }, + { V4L2_DV_BT_DMT_800X600P75, 0x03, 0x00 }, + { V4L2_DV_BT_DMT_800X600P85, 0x04, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P60, 0x0c, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P70, 0x0d, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P75, 0x0e, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P85, 0x0f, 0x00 }, + { V4L2_DV_BT_DMT_1280X1024P60, 0x05, 0x00 }, + { V4L2_DV_BT_DMT_1280X1024P75, 0x06, 0x00 }, + { }, +}; + +/* ----------------------------------------------------------------------- */ + +static inline struct adv7842_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7842_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7842_state, hdl)->sd; +} + +static inline unsigned hblanking(const struct v4l2_bt_timings *t) +{ + return V4L2_DV_BT_BLANKING_WIDTH(t); +} + +static inline unsigned htotal(const struct v4l2_bt_timings *t) +{ + return V4L2_DV_BT_FRAME_WIDTH(t); +} + +static inline unsigned vblanking(const struct v4l2_bt_timings *t) +{ + return V4L2_DV_BT_BLANKING_HEIGHT(t); +} + +static inline unsigned vtotal(const struct v4l2_bt_timings *t) +{ + return V4L2_DV_BT_FRAME_HEIGHT(t); +} + + +/* ----------------------------------------------------------------------- */ + +static s32 adv_smbus_read_byte_data_check(struct i2c_client *client, + u8 command, bool check) +{ + union i2c_smbus_data data; + + if (!i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data)) + return data.byte; + if (check) + v4l_err(client, "error reading %02x, %02x\n", + client->addr, command); + return -EIO; +} + +static s32 adv_smbus_read_byte_data(struct i2c_client *client, u8 command) +{ + int i; + + for (i = 0; i < 3; i++) { + int ret = adv_smbus_read_byte_data_check(client, command, true); + + if (ret >= 0) { + if (i) + v4l_err(client, "read ok after %d retries\n", i); + return ret; + } + } + v4l_err(client, "read failed\n"); + return -EIO; +} + +static s32 adv_smbus_write_byte_data(struct i2c_client *client, + u8 command, u8 value) +{ + union i2c_smbus_data data; + int err; + int i; + + data.byte = value; + for (i = 0; i < 3; i++) { + err = i2c_smbus_xfer(client->adapter, client->addr, + client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data); + if (!err) + break; + } + if (err < 0) + v4l_err(client, "error writing %02x, %02x, %02x\n", + client->addr, command, value); + return err; +} + +static void adv_smbus_write_byte_no_check(struct i2c_client *client, + u8 command, u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + + i2c_smbus_xfer(client->adapter, client->addr, + client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data); +} + +static s32 adv_smbus_write_i2c_block_data(struct i2c_client *client, + u8 command, unsigned length, const u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(data.block + 1, values, length); + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); +} + +/* ----------------------------------------------------------------------- */ + +static inline int io_read(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return adv_smbus_read_byte_data(client, reg); +} + +static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return adv_smbus_write_byte_data(client, reg, val); +} + +static inline int io_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return io_write(sd, reg, (io_read(sd, reg) & mask) | val); +} + +static inline int avlink_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_avlink, reg); +} + +static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_avlink, reg, val); +} + +static inline int cec_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_cec, reg); +} + +static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_cec, reg, val); +} + +static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val); +} + +static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_infoframe, reg); +} + +static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_infoframe, reg, val); +} + +static inline int sdp_io_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_sdp_io, reg); +} + +static inline int sdp_io_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_sdp_io, reg, val); +} + +static inline int sdp_io_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return sdp_io_write(sd, reg, (sdp_io_read(sd, reg) & mask) | val); +} + +static inline int sdp_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_sdp, reg); +} + +static inline int sdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_sdp, reg, val); +} + +static inline int sdp_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return sdp_write(sd, reg, (sdp_read(sd, reg) & mask) | val); +} + +static inline int afe_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_afe, reg); +} + +static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_afe, reg, val); +} + +static inline int afe_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return afe_write(sd, reg, (afe_read(sd, reg) & mask) | val); +} + +static inline int rep_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_repeater, reg); +} + +static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_repeater, reg, val); +} + +static inline int rep_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return rep_write(sd, reg, (rep_read(sd, reg) & mask) | val); +} + +static inline int edid_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_edid, reg); +} + +static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_edid, reg, val); +} + +static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_hdmi, reg); +} + +static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val); +} + +static inline int cp_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_cp, reg); +} + +static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_cp, reg, val); +} + +static inline int cp_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return cp_write(sd, reg, (cp_read(sd, reg) & mask) | val); +} + +static inline int vdp_read(struct v4l2_subdev *sd, u8 reg) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_read_byte_data(state->i2c_vdp, reg); +} + +static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct adv7842_state *state = to_state(sd); + + return adv_smbus_write_byte_data(state->i2c_vdp, reg, val); +} + +static void main_reset(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + adv_smbus_write_byte_no_check(client, 0xff, 0x80); + + mdelay(2); +} + +/* ----------------------------------------------------------------------- */ + +static inline bool is_digital_input(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + + return state->mode == ADV7842_MODE_HDMI; +} + +static const struct v4l2_dv_timings_cap adv7842_timings_cap_analog = { + .type = V4L2_DV_BT_656_1120, + .bt = { + .max_width = 1920, + .max_height = 1200, + .min_pixelclock = 25000000, + .max_pixelclock = 170000000, + .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, + .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM, + }, +}; + +static const struct v4l2_dv_timings_cap adv7842_timings_cap_digital = { + .type = V4L2_DV_BT_656_1120, + .bt = { + .max_width = 1920, + .max_height = 1200, + .min_pixelclock = 25000000, + .max_pixelclock = 225000000, + .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, + .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM, + }, +}; + +static inline const struct v4l2_dv_timings_cap * +adv7842_get_dv_timings_cap(struct v4l2_subdev *sd) +{ + return is_digital_input(sd) ? &adv7842_timings_cap_digital : + &adv7842_timings_cap_analog; +} + +/* ----------------------------------------------------------------------- */ + +static void adv7842_delayed_work_enable_hotplug(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct adv7842_state *state = container_of(dwork, + struct adv7842_state, delayed_work_enable_hotplug); + struct v4l2_subdev *sd = &state->sd; + int present = state->hdmi_edid.present; + u8 mask = 0; + + v4l2_dbg(2, debug, sd, "%s: enable hotplug on ports: 0x%x\n", + __func__, present); + + if (present & 0x1) + mask |= 0x20; /* port A */ + if (present & 0x2) + mask |= 0x10; /* port B */ + io_write_and_or(sd, 0x20, 0xcf, mask); +} + +static int edid_write_vga_segment(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7842_state *state = to_state(sd); + const u8 *val = state->vga_edid.edid; + int err = 0; + int i; + + v4l2_dbg(2, debug, sd, "%s: write EDID on VGA port\n", __func__); + + /* HPA disable on port A and B */ + io_write_and_or(sd, 0x20, 0xcf, 0x00); + + /* Disable I2C access to internal EDID ram from VGA DDC port */ + rep_write_and_or(sd, 0x7f, 0x7f, 0x00); + + /* edid segment pointer '1' for VGA port */ + rep_write_and_or(sd, 0x77, 0xef, 0x10); + + for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX) + err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, + I2C_SMBUS_BLOCK_MAX, val + i); + if (err) + return err; + + /* Calculates the checksums and enables I2C access + * to internal EDID ram from VGA DDC port. + */ + rep_write_and_or(sd, 0x7f, 0x7f, 0x80); + + for (i = 0; i < 1000; i++) { + if (rep_read(sd, 0x79) & 0x20) + break; + mdelay(1); + } + if (i == 1000) { + v4l_err(client, "error enabling edid on VGA port\n"); + return -EIO; + } + + /* enable hotplug after 200 ms */ + queue_delayed_work(state->work_queues, + &state->delayed_work_enable_hotplug, HZ / 5); + + return 0; +} + +static int edid_spa_location(const u8 *edid) +{ + u8 d; + + /* + * TODO, improve and update for other CEA extensions + * currently only for 1 segment (256 bytes), + * i.e. 1 extension block and CEA revision 3. + */ + if ((edid[0x7e] != 1) || + (edid[0x80] != 0x02) || + (edid[0x81] != 0x03)) { + return -EINVAL; + } + /* + * search Vendor Specific Data Block (tag 3) + */ + d = edid[0x82] & 0x7f; + if (d > 4) { + int i = 0x84; + int end = 0x80 + d; + do { + u8 tag = edid[i]>>5; + u8 len = edid[i] & 0x1f; + + if ((tag == 3) && (len >= 5)) + return i + 4; + i += len + 1; + } while (i < end); + } + return -EINVAL; +} + +static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7842_state *state = to_state(sd); + const u8 *val = state->hdmi_edid.edid; + u8 cur_mask = rep_read(sd, 0x77) & 0x0c; + u8 mask = port == 0 ? 0x4 : 0x8; + int spa_loc = edid_spa_location(val); + int err = 0; + int i; + + v4l2_dbg(2, debug, sd, "%s: write EDID on port %d (spa at 0x%x)\n", + __func__, port, spa_loc); + + /* HPA disable on port A and B */ + io_write_and_or(sd, 0x20, 0xcf, 0x00); + + /* Disable I2C access to internal EDID ram from HDMI DDC ports */ + rep_write_and_or(sd, 0x77, 0xf3, 0x00); + + /* edid segment pointer '0' for HDMI ports */ + rep_write_and_or(sd, 0x77, 0xef, 0x00); + + for (i = 0; !err && i < 256; i += I2C_SMBUS_BLOCK_MAX) + err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, + I2C_SMBUS_BLOCK_MAX, val + i); + if (err) + return err; + + if (spa_loc > 0) { + if (port == 0) { + /* port A SPA */ + rep_write(sd, 0x72, val[spa_loc]); + rep_write(sd, 0x73, val[spa_loc + 1]); + } else { + /* port B SPA */ + rep_write(sd, 0x74, val[spa_loc]); + rep_write(sd, 0x75, val[spa_loc + 1]); + } + rep_write(sd, 0x76, spa_loc); + } else { + /* default register values for SPA */ + if (port == 0) { + /* port A SPA */ + rep_write(sd, 0x72, 0); + rep_write(sd, 0x73, 0); + } else { + /* port B SPA */ + rep_write(sd, 0x74, 0); + rep_write(sd, 0x75, 0); + } + rep_write(sd, 0x76, 0xc0); + } + rep_write_and_or(sd, 0x77, 0xbf, 0x00); + + /* Calculates the checksums and enables I2C access to internal + * EDID ram from HDMI DDC ports + */ + rep_write_and_or(sd, 0x77, 0xf3, mask | cur_mask); + + for (i = 0; i < 1000; i++) { + if (rep_read(sd, 0x7d) & mask) + break; + mdelay(1); + } + if (i == 1000) { + v4l_err(client, "error enabling edid on port %d\n", port); + return -EIO; + } + + /* enable hotplug after 200 ms */ + queue_delayed_work(state->work_queues, + &state->delayed_work_enable_hotplug, HZ / 5); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static void adv7842_inv_register(struct v4l2_subdev *sd) +{ + v4l2_info(sd, "0x000-0x0ff: IO Map\n"); + v4l2_info(sd, "0x100-0x1ff: AVLink Map\n"); + v4l2_info(sd, "0x200-0x2ff: CEC Map\n"); + v4l2_info(sd, "0x300-0x3ff: InfoFrame Map\n"); + v4l2_info(sd, "0x400-0x4ff: SDP_IO Map\n"); + v4l2_info(sd, "0x500-0x5ff: SDP Map\n"); + v4l2_info(sd, "0x600-0x6ff: AFE Map\n"); + v4l2_info(sd, "0x700-0x7ff: Repeater Map\n"); + v4l2_info(sd, "0x800-0x8ff: EDID Map\n"); + v4l2_info(sd, "0x900-0x9ff: HDMI Map\n"); + v4l2_info(sd, "0xa00-0xaff: CP Map\n"); + v4l2_info(sd, "0xb00-0xbff: VDP Map\n"); +} + +static int adv7842_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + reg->size = 1; + switch (reg->reg >> 8) { + case 0: + reg->val = io_read(sd, reg->reg & 0xff); + break; + case 1: + reg->val = avlink_read(sd, reg->reg & 0xff); + break; + case 2: + reg->val = cec_read(sd, reg->reg & 0xff); + break; + case 3: + reg->val = infoframe_read(sd, reg->reg & 0xff); + break; + case 4: + reg->val = sdp_io_read(sd, reg->reg & 0xff); + break; + case 5: + reg->val = sdp_read(sd, reg->reg & 0xff); + break; + case 6: + reg->val = afe_read(sd, reg->reg & 0xff); + break; + case 7: + reg->val = rep_read(sd, reg->reg & 0xff); + break; + case 8: + reg->val = edid_read(sd, reg->reg & 0xff); + break; + case 9: + reg->val = hdmi_read(sd, reg->reg & 0xff); + break; + case 0xa: + reg->val = cp_read(sd, reg->reg & 0xff); + break; + case 0xb: + reg->val = vdp_read(sd, reg->reg & 0xff); + break; + default: + v4l2_info(sd, "Register %03llx not supported\n", reg->reg); + adv7842_inv_register(sd); + break; + } + return 0; +} + +static int adv7842_s_register(struct v4l2_subdev *sd, + const struct v4l2_dbg_register *reg) +{ + u8 val = reg->val & 0xff; + + switch (reg->reg >> 8) { + case 0: + io_write(sd, reg->reg & 0xff, val); + break; + case 1: + avlink_write(sd, reg->reg & 0xff, val); + break; + case 2: + cec_write(sd, reg->reg & 0xff, val); + break; + case 3: + infoframe_write(sd, reg->reg & 0xff, val); + break; + case 4: + sdp_io_write(sd, reg->reg & 0xff, val); + break; + case 5: + sdp_write(sd, reg->reg & 0xff, val); + break; + case 6: + afe_write(sd, reg->reg & 0xff, val); + break; + case 7: + rep_write(sd, reg->reg & 0xff, val); + break; + case 8: + edid_write(sd, reg->reg & 0xff, val); + break; + case 9: + hdmi_write(sd, reg->reg & 0xff, val); + break; + case 0xa: + cp_write(sd, reg->reg & 0xff, val); + break; + case 0xb: + vdp_write(sd, reg->reg & 0xff, val); + break; + default: + v4l2_info(sd, "Register %03llx not supported\n", reg->reg); + adv7842_inv_register(sd); + break; + } + return 0; +} +#endif + +static int adv7842_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + int prev = v4l2_ctrl_g_ctrl(state->detect_tx_5v_ctrl); + u8 reg_io_6f = io_read(sd, 0x6f); + int val = 0; + + if (reg_io_6f & 0x02) + val |= 1; /* port A */ + if (reg_io_6f & 0x01) + val |= 2; /* port B */ + + v4l2_dbg(1, debug, sd, "%s: 0x%x -> 0x%x\n", __func__, prev, val); + + if (val != prev) + return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, val); + return 0; +} + +static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, + u8 prim_mode, + const struct adv7842_video_standards *predef_vid_timings, + const struct v4l2_dv_timings *timings) +{ + int i; + + for (i = 0; predef_vid_timings[i].timings.bt.width; i++) { + if (!v4l2_match_dv_timings(timings, &predef_vid_timings[i].timings, + is_digital_input(sd) ? 250000 : 1000000)) + continue; + /* video std */ + io_write(sd, 0x00, predef_vid_timings[i].vid_std); + /* v_freq and prim mode */ + io_write(sd, 0x01, (predef_vid_timings[i].v_freq << 4) + prim_mode); + return 0; + } + + return -1; +} + +static int configure_predefined_video_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct adv7842_state *state = to_state(sd); + int err; + + v4l2_dbg(1, debug, sd, "%s\n", __func__); + + /* reset to default values */ + io_write(sd, 0x16, 0x43); + io_write(sd, 0x17, 0x5a); + /* disable embedded syncs for auto graphics mode */ + cp_write_and_or(sd, 0x81, 0xef, 0x00); + cp_write(sd, 0x26, 0x00); + cp_write(sd, 0x27, 0x00); + cp_write(sd, 0x28, 0x00); + cp_write(sd, 0x29, 0x00); + cp_write(sd, 0x8f, 0x00); + cp_write(sd, 0x90, 0x00); + cp_write(sd, 0xa5, 0x00); + cp_write(sd, 0xa6, 0x00); + cp_write(sd, 0xa7, 0x00); + cp_write(sd, 0xab, 0x00); + cp_write(sd, 0xac, 0x00); + + switch (state->mode) { + case ADV7842_MODE_COMP: + case ADV7842_MODE_RGB: + err = find_and_set_predefined_video_timings(sd, + 0x01, adv7842_prim_mode_comp, timings); + if (err) + err = find_and_set_predefined_video_timings(sd, + 0x02, adv7842_prim_mode_gr, timings); + break; + case ADV7842_MODE_HDMI: + err = find_and_set_predefined_video_timings(sd, + 0x05, adv7842_prim_mode_hdmi_comp, timings); + if (err) + err = find_and_set_predefined_video_timings(sd, + 0x06, adv7842_prim_mode_hdmi_gr, timings); + break; + default: + v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", + __func__, state->mode); + err = -1; + break; + } + + + return err; +} + +static void configure_custom_video_timings(struct v4l2_subdev *sd, + const struct v4l2_bt_timings *bt) +{ + struct adv7842_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u32 width = htotal(bt); + u32 height = vtotal(bt); + u16 cp_start_sav = bt->hsync + bt->hbackporch - 4; + u16 cp_start_eav = width - bt->hfrontporch; + u16 cp_start_vbi = height - bt->vfrontporch + 1; + u16 cp_end_vbi = bt->vsync + bt->vbackporch + 1; + u16 ch1_fr_ll = (((u32)bt->pixelclock / 100) > 0) ? + ((width * (ADV7842_fsc / 100)) / ((u32)bt->pixelclock / 100)) : 0; + const u8 pll[2] = { + 0xc0 | ((width >> 8) & 0x1f), + width & 0xff + }; + + v4l2_dbg(2, debug, sd, "%s\n", __func__); + + switch (state->mode) { + case ADV7842_MODE_COMP: + case ADV7842_MODE_RGB: + /* auto graphics */ + io_write(sd, 0x00, 0x07); /* video std */ + io_write(sd, 0x01, 0x02); /* prim mode */ + /* enable embedded syncs for auto graphics mode */ + cp_write_and_or(sd, 0x81, 0xef, 0x10); + + /* Should only be set in auto-graphics mode [REF_02, p. 91-92] */ + /* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */ + /* IO-map reg. 0x16 and 0x17 should be written in sequence */ + if (adv_smbus_write_i2c_block_data(client, 0x16, 2, pll)) { + v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n"); + break; + } + + /* active video - horizontal timing */ + cp_write(sd, 0x26, (cp_start_sav >> 8) & 0xf); + cp_write(sd, 0x27, (cp_start_sav & 0xff)); + cp_write(sd, 0x28, (cp_start_eav >> 8) & 0xf); + cp_write(sd, 0x29, (cp_start_eav & 0xff)); + + /* active video - vertical timing */ + cp_write(sd, 0xa5, (cp_start_vbi >> 4) & 0xff); + cp_write(sd, 0xa6, ((cp_start_vbi & 0xf) << 4) | + ((cp_end_vbi >> 8) & 0xf)); + cp_write(sd, 0xa7, cp_end_vbi & 0xff); + break; + case ADV7842_MODE_HDMI: + /* set default prim_mode/vid_std for HDMI + accoring to [REF_03, c. 4.2] */ + io_write(sd, 0x00, 0x02); /* video std */ + io_write(sd, 0x01, 0x06); /* prim mode */ + break; + default: + v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", + __func__, state->mode); + break; + } + + cp_write(sd, 0x8f, (ch1_fr_ll >> 8) & 0x7); + cp_write(sd, 0x90, ch1_fr_ll & 0xff); + cp_write(sd, 0xab, (height >> 4) & 0xff); + cp_write(sd, 0xac, (height & 0x0f) << 4); +} + +static void set_rgb_quantization_range(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + + switch (state->rgb_quantization_range) { + case V4L2_DV_RGB_RANGE_AUTO: + /* automatic */ + if (is_digital_input(sd) && !(hdmi_read(sd, 0x05) & 0x80)) { + /* receiving DVI-D signal */ + + /* ADV7842 selects RGB limited range regardless of + input format (CE/IT) in automatic mode */ + if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { + /* RGB limited range (16-235) */ + io_write_and_or(sd, 0x02, 0x0f, 0x00); + + } else { + /* RGB full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); + } + } else { + /* receiving HDMI or analog signal, set automode */ + io_write_and_or(sd, 0x02, 0x0f, 0xf0); + } + break; + case V4L2_DV_RGB_RANGE_LIMITED: + /* RGB limited range (16-235) */ + io_write_and_or(sd, 0x02, 0x0f, 0x00); + break; + case V4L2_DV_RGB_RANGE_FULL: + /* RGB full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); + break; + } +} + +static int adv7842_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct adv7842_state *state = to_state(sd); + + /* TODO SDP ctrls + contrast/brightness/hue/free run is acting a bit strange, + not sure if sdp csc is correct. + */ + switch (ctrl->id) { + /* standard ctrls */ + case V4L2_CID_BRIGHTNESS: + cp_write(sd, 0x3c, ctrl->val); + sdp_write(sd, 0x14, ctrl->val); + /* ignore lsb sdp 0x17[3:2] */ + return 0; + case V4L2_CID_CONTRAST: + cp_write(sd, 0x3a, ctrl->val); + sdp_write(sd, 0x13, ctrl->val); + /* ignore lsb sdp 0x17[1:0] */ + return 0; + case V4L2_CID_SATURATION: + cp_write(sd, 0x3b, ctrl->val); + sdp_write(sd, 0x15, ctrl->val); + /* ignore lsb sdp 0x17[5:4] */ + return 0; + case V4L2_CID_HUE: + cp_write(sd, 0x3d, ctrl->val); + sdp_write(sd, 0x16, ctrl->val); + /* ignore lsb sdp 0x17[7:6] */ + return 0; + /* custom ctrls */ + case V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE: + afe_write(sd, 0xc8, ctrl->val); + return 0; + case V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL: + cp_write_and_or(sd, 0xbf, ~0x04, (ctrl->val << 2)); + sdp_write_and_or(sd, 0xdd, ~0x04, (ctrl->val << 2)); + return 0; + case V4L2_CID_ADV_RX_FREE_RUN_COLOR: { + u8 R = (ctrl->val & 0xff0000) >> 16; + u8 G = (ctrl->val & 0x00ff00) >> 8; + u8 B = (ctrl->val & 0x0000ff); + /* RGB -> YUV, numerical approximation */ + int Y = 66 * R + 129 * G + 25 * B; + int U = -38 * R - 74 * G + 112 * B; + int V = 112 * R - 94 * G - 18 * B; + + /* Scale down to 8 bits with rounding */ + Y = (Y + 128) >> 8; + U = (U + 128) >> 8; + V = (V + 128) >> 8; + /* make U,V positive */ + Y += 16; + U += 128; + V += 128; + + v4l2_dbg(1, debug, sd, "R %x, G %x, B %x\n", R, G, B); + v4l2_dbg(1, debug, sd, "Y %x, U %x, V %x\n", Y, U, V); + + /* CP */ + cp_write(sd, 0xc1, R); + cp_write(sd, 0xc0, G); + cp_write(sd, 0xc2, B); + /* SDP */ + sdp_write(sd, 0xde, Y); + sdp_write(sd, 0xdf, (V & 0xf0) | ((U >> 4) & 0x0f)); + return 0; + } + case V4L2_CID_DV_RX_RGB_RANGE: + state->rgb_quantization_range = ctrl->val; + set_rgb_quantization_range(sd); + return 0; + } + return -EINVAL; +} + +static inline bool no_power(struct v4l2_subdev *sd) +{ + return io_read(sd, 0x0c) & 0x24; +} + +static inline bool no_cp_signal(struct v4l2_subdev *sd) +{ + return ((cp_read(sd, 0xb5) & 0xd0) != 0xd0) || !(cp_read(sd, 0xb1) & 0x80); +} + +static inline bool is_hdmi(struct v4l2_subdev *sd) +{ + return hdmi_read(sd, 0x05) & 0x80; +} + +static int adv7842_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct adv7842_state *state = to_state(sd); + + *status = 0; + + if (io_read(sd, 0x0c) & 0x24) + *status |= V4L2_IN_ST_NO_POWER; + + if (state->mode == ADV7842_MODE_SDP) { + /* status from SDP block */ + if (!(sdp_read(sd, 0x5A) & 0x01)) + *status |= V4L2_IN_ST_NO_SIGNAL; + + v4l2_dbg(1, debug, sd, "%s: SDP status = 0x%x\n", + __func__, *status); + return 0; + } + /* status from CP block */ + if ((cp_read(sd, 0xb5) & 0xd0) != 0xd0 || + !(cp_read(sd, 0xb1) & 0x80)) + /* TODO channel 2 */ + *status |= V4L2_IN_ST_NO_SIGNAL; + + if (is_digital_input(sd) && ((io_read(sd, 0x74) & 0x03) != 0x03)) + *status |= V4L2_IN_ST_NO_SIGNAL; + + v4l2_dbg(1, debug, sd, "%s: CP status = 0x%x\n", + __func__, *status); + + return 0; +} + +struct stdi_readback { + u16 bl, lcf, lcvs; + u8 hs_pol, vs_pol; + bool interlaced; +}; + +static int stdi2dv_timings(struct v4l2_subdev *sd, + struct stdi_readback *stdi, + struct v4l2_dv_timings *timings) +{ + struct adv7842_state *state = to_state(sd); + u32 hfreq = (ADV7842_fsc * 8) / stdi->bl; + u32 pix_clk; + int i; + + for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { + const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; + + if (!v4l2_valid_dv_timings(&v4l2_dv_timings_presets[i], + adv7842_get_dv_timings_cap(sd), + adv7842_check_dv_timings, NULL)) + continue; + if (vtotal(bt) != stdi->lcf + 1) + continue; + if (bt->vsync != stdi->lcvs) + continue; + + pix_clk = hfreq * htotal(bt); + + if ((pix_clk < bt->pixelclock + 1000000) && + (pix_clk > bt->pixelclock - 1000000)) { + *timings = v4l2_dv_timings_presets[i]; + return 0; + } + } + + if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, + (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | + (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), + timings)) + return 0; + if (v4l2_detect_gtf(stdi->lcf + 1, hfreq, stdi->lcvs, + (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | + (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), + state->aspect_ratio, timings)) + return 0; + + v4l2_dbg(2, debug, sd, + "%s: No format candidate found for lcvs = %d, lcf=%d, bl = %d, %chsync, %cvsync\n", + __func__, stdi->lcvs, stdi->lcf, stdi->bl, + stdi->hs_pol, stdi->vs_pol); + return -1; +} + +static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi) +{ + u32 status; + + adv7842_g_input_status(sd, &status); + if (status & V4L2_IN_ST_NO_SIGNAL) { + v4l2_dbg(2, debug, sd, "%s: no signal\n", __func__); + return -ENOLINK; + } + + stdi->bl = ((cp_read(sd, 0xb1) & 0x3f) << 8) | cp_read(sd, 0xb2); + stdi->lcf = ((cp_read(sd, 0xb3) & 0x7) << 8) | cp_read(sd, 0xb4); + stdi->lcvs = cp_read(sd, 0xb3) >> 3; + + if ((cp_read(sd, 0xb5) & 0x80) && ((cp_read(sd, 0xb5) & 0x03) == 0x01)) { + stdi->hs_pol = ((cp_read(sd, 0xb5) & 0x10) ? + ((cp_read(sd, 0xb5) & 0x08) ? '+' : '-') : 'x'); + stdi->vs_pol = ((cp_read(sd, 0xb5) & 0x40) ? + ((cp_read(sd, 0xb5) & 0x20) ? '+' : '-') : 'x'); + } else { + stdi->hs_pol = 'x'; + stdi->vs_pol = 'x'; + } + stdi->interlaced = (cp_read(sd, 0xb1) & 0x40) ? true : false; + + if (stdi->lcf < 239 || stdi->bl < 8 || stdi->bl == 0x3fff) { + v4l2_dbg(2, debug, sd, "%s: invalid signal\n", __func__); + return -ENOLINK; + } + + v4l2_dbg(2, debug, sd, + "%s: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, %chsync, %cvsync, %s\n", + __func__, stdi->lcf, stdi->bl, stdi->lcvs, + stdi->hs_pol, stdi->vs_pol, + stdi->interlaced ? "interlaced" : "progressive"); + + return 0; +} + +static int adv7842_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + return v4l2_enum_dv_timings_cap(timings, + adv7842_get_dv_timings_cap(sd), adv7842_check_dv_timings, NULL); +} + +static int adv7842_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + *cap = *adv7842_get_dv_timings_cap(sd); + return 0; +} + +/* Fill the optional fields .standards and .flags in struct v4l2_dv_timings + if the format is listed in adv7604_timings[] */ +static void adv7842_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + v4l2_find_dv_timings_cap(timings, adv7842_get_dv_timings_cap(sd), + is_digital_input(sd) ? 250000 : 1000000, + adv7842_check_dv_timings, NULL); +} + +static int adv7842_query_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct adv7842_state *state = to_state(sd); + struct v4l2_bt_timings *bt = &timings->bt; + struct stdi_readback stdi = { 0 }; + + /* SDP block */ + if (state->mode == ADV7842_MODE_SDP) + return -ENODATA; + + /* read STDI */ + if (read_stdi(sd, &stdi)) { + v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); + return -ENOLINK; + } + bt->interlaced = stdi.interlaced ? + V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; + bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) | + ((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0); + bt->vsync = stdi.lcvs; + + if (is_digital_input(sd)) { + bool lock = hdmi_read(sd, 0x04) & 0x02; + bool interlaced = hdmi_read(sd, 0x0b) & 0x20; + unsigned w = (hdmi_read(sd, 0x07) & 0x1f) * 256 + hdmi_read(sd, 0x08); + unsigned h = (hdmi_read(sd, 0x09) & 0x1f) * 256 + hdmi_read(sd, 0x0a); + unsigned w_total = (hdmi_read(sd, 0x1e) & 0x3f) * 256 + + hdmi_read(sd, 0x1f); + unsigned h_total = ((hdmi_read(sd, 0x26) & 0x3f) * 256 + + hdmi_read(sd, 0x27)) / 2; + unsigned freq = (((hdmi_read(sd, 0x51) << 1) + + (hdmi_read(sd, 0x52) >> 7)) * 1000000) + + ((hdmi_read(sd, 0x52) & 0x7f) * 1000000) / 128; + int i; + + if (is_hdmi(sd)) { + /* adjust for deep color mode */ + freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0)>>6) * 2 + 8); + } + + /* No lock? */ + if (!lock) { + v4l2_dbg(1, debug, sd, "%s: no lock on TMDS signal\n", __func__); + return -ENOLCK; + } + /* Interlaced? */ + if (interlaced) { + v4l2_dbg(1, debug, sd, "%s: interlaced video not supported\n", __func__); + return -ERANGE; + } + + for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { + const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; + + if (!v4l2_valid_dv_timings(&v4l2_dv_timings_presets[i], + adv7842_get_dv_timings_cap(sd), + adv7842_check_dv_timings, NULL)) + continue; + if (w_total != htotal(bt) || h_total != vtotal(bt)) + continue; + + if (w != bt->width || h != bt->height) + continue; + + if (abs(freq - bt->pixelclock) > 1000000) + continue; + *timings = v4l2_dv_timings_presets[i]; + return 0; + } + + timings->type = V4L2_DV_BT_656_1120; + + bt->width = w; + bt->height = h; + bt->interlaced = (hdmi_read(sd, 0x0b) & 0x20) ? + V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; + bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? + V4L2_DV_VSYNC_POS_POL : 0) | ((hdmi_read(sd, 0x05) & 0x20) ? + V4L2_DV_HSYNC_POS_POL : 0); + bt->pixelclock = (((hdmi_read(sd, 0x51) << 1) + + (hdmi_read(sd, 0x52) >> 7)) * 1000000) + + ((hdmi_read(sd, 0x52) & 0x7f) * 1000000) / 128; + bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x1f) * 256 + + hdmi_read(sd, 0x21); + bt->hsync = (hdmi_read(sd, 0x22) & 0x1f) * 256 + + hdmi_read(sd, 0x23); + bt->hbackporch = (hdmi_read(sd, 0x24) & 0x1f) * 256 + + hdmi_read(sd, 0x25); + bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x3f) * 256 + + hdmi_read(sd, 0x2b)) / 2; + bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x3f) * 256 + + hdmi_read(sd, 0x2d)) / 2; + bt->vsync = ((hdmi_read(sd, 0x2e) & 0x3f) * 256 + + hdmi_read(sd, 0x2f)) / 2; + bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x3f) * 256 + + hdmi_read(sd, 0x31)) / 2; + bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x3f) * 256 + + hdmi_read(sd, 0x33)) / 2; + bt->il_vbackporch = ((hdmi_read(sd, 0x34) & 0x3f) * 256 + + hdmi_read(sd, 0x35)) / 2; + + bt->standards = 0; + bt->flags = 0; + } else { + /* Interlaced? */ + if (stdi.interlaced) { + v4l2_dbg(1, debug, sd, "%s: interlaced video not supported\n", __func__); + return -ERANGE; + } + + if (stdi2dv_timings(sd, &stdi, timings)) { + v4l2_dbg(1, debug, sd, "%s: format not supported\n", __func__); + return -ERANGE; + } + } + + if (debug > 1) + v4l2_print_dv_timings(sd->name, "adv7842_query_dv_timings: ", + timings, true); + return 0; +} + +static int adv7842_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct adv7842_state *state = to_state(sd); + struct v4l2_bt_timings *bt; + int err; + + if (state->mode == ADV7842_MODE_SDP) + return -ENODATA; + + bt = &timings->bt; + + if (!v4l2_valid_dv_timings(timings, adv7842_get_dv_timings_cap(sd), + adv7842_check_dv_timings, NULL)) + return -ERANGE; + + adv7842_fill_optional_dv_timings_fields(sd, timings); + + state->timings = *timings; + + cp_write(sd, 0x91, bt->interlaced ? 0x50 : 0x10); + + /* Use prim_mode and vid_std when available */ + err = configure_predefined_video_timings(sd, timings); + if (err) { + /* custom settings when the video format + does not have prim_mode/vid_std */ + configure_custom_video_timings(sd, bt); + } + + set_rgb_quantization_range(sd); + + + if (debug > 1) + v4l2_print_dv_timings(sd->name, "adv7842_s_dv_timings: ", + timings, true); + return 0; +} + +static int adv7842_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct adv7842_state *state = to_state(sd); + + if (state->mode == ADV7842_MODE_SDP) + return -ENODATA; + *timings = state->timings; + return 0; +} + +static void enable_input(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + switch (state->mode) { + case ADV7842_MODE_SDP: + case ADV7842_MODE_COMP: + case ADV7842_MODE_RGB: + /* enable */ + io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */ + break; + case ADV7842_MODE_HDMI: + /* enable */ + hdmi_write(sd, 0x1a, 0x0a); /* Unmute audio */ + hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */ + io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */ + break; + default: + v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", + __func__, state->mode); + break; + } +} + +static void disable_input(struct v4l2_subdev *sd) +{ + /* disable */ + io_write(sd, 0x15, 0xbe); /* Tristate all outputs from video core */ + hdmi_write(sd, 0x1a, 0x1a); /* Mute audio */ + hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */ +} + +static void sdp_csc_coeff(struct v4l2_subdev *sd, + const struct adv7842_sdp_csc_coeff *c) +{ + /* csc auto/manual */ + sdp_io_write_and_or(sd, 0xe0, 0xbf, c->manual ? 0x00 : 0x40); + + if (!c->manual) + return; + + /* csc scaling */ + sdp_io_write_and_or(sd, 0xe0, 0x7f, c->scaling == 2 ? 0x80 : 0x00); + + /* A coeff */ + sdp_io_write_and_or(sd, 0xe0, 0xe0, c->A1 >> 8); + sdp_io_write(sd, 0xe1, c->A1); + sdp_io_write_and_or(sd, 0xe2, 0xe0, c->A2 >> 8); + sdp_io_write(sd, 0xe3, c->A2); + sdp_io_write_and_or(sd, 0xe4, 0xe0, c->A3 >> 8); + sdp_io_write(sd, 0xe5, c->A3); + + /* A scale */ + sdp_io_write_and_or(sd, 0xe6, 0x80, c->A4 >> 8); + sdp_io_write(sd, 0xe7, c->A4); + + /* B coeff */ + sdp_io_write_and_or(sd, 0xe8, 0xe0, c->B1 >> 8); + sdp_io_write(sd, 0xe9, c->B1); + sdp_io_write_and_or(sd, 0xea, 0xe0, c->B2 >> 8); + sdp_io_write(sd, 0xeb, c->B2); + sdp_io_write_and_or(sd, 0xec, 0xe0, c->B3 >> 8); + sdp_io_write(sd, 0xed, c->B3); + + /* B scale */ + sdp_io_write_and_or(sd, 0xee, 0x80, c->B4 >> 8); + sdp_io_write(sd, 0xef, c->B4); + + /* C coeff */ + sdp_io_write_and_or(sd, 0xf0, 0xe0, c->C1 >> 8); + sdp_io_write(sd, 0xf1, c->C1); + sdp_io_write_and_or(sd, 0xf2, 0xe0, c->C2 >> 8); + sdp_io_write(sd, 0xf3, c->C2); + sdp_io_write_and_or(sd, 0xf4, 0xe0, c->C3 >> 8); + sdp_io_write(sd, 0xf5, c->C3); + + /* C scale */ + sdp_io_write_and_or(sd, 0xf6, 0x80, c->C4 >> 8); + sdp_io_write(sd, 0xf7, c->C4); +} + +static void select_input(struct v4l2_subdev *sd, + enum adv7842_vid_std_select vid_std_select) +{ + struct adv7842_state *state = to_state(sd); + + switch (state->mode) { + case ADV7842_MODE_SDP: + io_write(sd, 0x00, vid_std_select); /* video std: CVBS or YC mode */ + io_write(sd, 0x01, 0); /* prim mode */ + /* enable embedded syncs for auto graphics mode */ + cp_write_and_or(sd, 0x81, 0xef, 0x10); + + afe_write(sd, 0x00, 0x00); /* power up ADC */ + afe_write(sd, 0xc8, 0x00); /* phase control */ + + io_write(sd, 0x19, 0x83); /* LLC DLL phase */ + io_write(sd, 0x33, 0x40); /* LLC DLL enable */ + + io_write(sd, 0xdd, 0x90); /* Manual 2x output clock */ + /* script says register 0xde, which don't exist in manual */ + + /* Manual analog input muxing mode, CVBS (6.4)*/ + afe_write_and_or(sd, 0x02, 0x7f, 0x80); + if (vid_std_select == ADV7842_SDP_VID_STD_CVBS_SD_4x1) { + afe_write(sd, 0x03, 0xa0); /* ADC0 to AIN10 (CVBS), ADC1 N/C*/ + afe_write(sd, 0x04, 0x00); /* ADC2 N/C,ADC3 N/C*/ + } else { + afe_write(sd, 0x03, 0xa0); /* ADC0 to AIN10 (CVBS), ADC1 N/C*/ + afe_write(sd, 0x04, 0xc0); /* ADC2 to AIN12, ADC3 N/C*/ + } + afe_write(sd, 0x0c, 0x1f); /* ADI recommend write */ + afe_write(sd, 0x12, 0x63); /* ADI recommend write */ + + sdp_io_write(sd, 0xb2, 0x60); /* Disable AV codes */ + sdp_io_write(sd, 0xc8, 0xe3); /* Disable Ancillary data */ + + /* SDP recommended settings */ + sdp_write(sd, 0x00, 0x3F); /* Autodetect PAL NTSC (not SECAM) */ + sdp_write(sd, 0x01, 0x00); /* Pedestal Off */ + + sdp_write(sd, 0x03, 0xE4); /* Manual VCR Gain Luma 0x40B */ + sdp_write(sd, 0x04, 0x0B); /* Manual Luma setting */ + sdp_write(sd, 0x05, 0xC3); /* Manual Chroma setting 0x3FE */ + sdp_write(sd, 0x06, 0xFE); /* Manual Chroma setting */ + sdp_write(sd, 0x12, 0x0D); /* Frame TBC,I_P, 3D comb enabled */ + sdp_write(sd, 0xA7, 0x00); /* ADI Recommended Write */ + sdp_io_write(sd, 0xB0, 0x00); /* Disable H and v blanking */ + + /* deinterlacer enabled and 3D comb */ + sdp_write_and_or(sd, 0x12, 0xf6, 0x09); + + sdp_write(sd, 0xdd, 0x08); /* free run auto */ + + break; + + case ADV7842_MODE_COMP: + case ADV7842_MODE_RGB: + /* Automatic analog input muxing mode */ + afe_write_and_or(sd, 0x02, 0x7f, 0x00); + /* set mode and select free run resolution */ + io_write(sd, 0x00, vid_std_select); /* video std */ + io_write(sd, 0x01, 0x02); /* prim mode */ + cp_write_and_or(sd, 0x81, 0xef, 0x10); /* enable embedded syncs + for auto graphics mode */ + + afe_write(sd, 0x00, 0x00); /* power up ADC */ + afe_write(sd, 0xc8, 0x00); /* phase control */ + + /* set ADI recommended settings for digitizer */ + /* "ADV7842 Register Settings Recommendations + * (rev. 1.8, November 2010)" p. 9. */ + afe_write(sd, 0x0c, 0x1f); /* ADC Range improvement */ + afe_write(sd, 0x12, 0x63); /* ADC Range improvement */ + + /* set to default gain for RGB */ + cp_write(sd, 0x73, 0x10); + cp_write(sd, 0x74, 0x04); + cp_write(sd, 0x75, 0x01); + cp_write(sd, 0x76, 0x00); + + cp_write(sd, 0x3e, 0x04); /* CP core pre-gain control */ + cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */ + cp_write(sd, 0x40, 0x5c); /* CP core pre-gain control. Graphics mode */ + break; + + case ADV7842_MODE_HDMI: + /* Automatic analog input muxing mode */ + afe_write_and_or(sd, 0x02, 0x7f, 0x00); + /* set mode and select free run resolution */ + if (state->hdmi_port_a) + hdmi_write(sd, 0x00, 0x02); /* select port A */ + else + hdmi_write(sd, 0x00, 0x03); /* select port B */ + io_write(sd, 0x00, vid_std_select); /* video std */ + io_write(sd, 0x01, 5); /* prim mode */ + cp_write_and_or(sd, 0x81, 0xef, 0x00); /* disable embedded syncs + for auto graphics mode */ + + /* set ADI recommended settings for HDMI: */ + /* "ADV7842 Register Settings Recommendations + * (rev. 1.8, November 2010)" p. 3. */ + hdmi_write(sd, 0xc0, 0x00); + hdmi_write(sd, 0x0d, 0x34); /* ADI recommended write */ + hdmi_write(sd, 0x3d, 0x10); /* ADI recommended write */ + hdmi_write(sd, 0x44, 0x85); /* TMDS PLL optimization */ + hdmi_write(sd, 0x46, 0x1f); /* ADI recommended write */ + hdmi_write(sd, 0x57, 0xb6); /* TMDS PLL optimization */ + hdmi_write(sd, 0x58, 0x03); /* TMDS PLL optimization */ + hdmi_write(sd, 0x60, 0x88); /* TMDS PLL optimization */ + hdmi_write(sd, 0x61, 0x88); /* TMDS PLL optimization */ + hdmi_write(sd, 0x6c, 0x18); /* Disable ISRC clearing bit, + Improve robustness */ + hdmi_write(sd, 0x75, 0x10); /* DDC drive strength */ + hdmi_write(sd, 0x85, 0x1f); /* equaliser */ + hdmi_write(sd, 0x87, 0x70); /* ADI recommended write */ + hdmi_write(sd, 0x89, 0x04); /* equaliser */ + hdmi_write(sd, 0x8a, 0x1e); /* equaliser */ + hdmi_write(sd, 0x93, 0x04); /* equaliser */ + hdmi_write(sd, 0x94, 0x1e); /* equaliser */ + hdmi_write(sd, 0x99, 0xa1); /* ADI recommended write */ + hdmi_write(sd, 0x9b, 0x09); /* ADI recommended write */ + hdmi_write(sd, 0x9d, 0x02); /* equaliser */ + + afe_write(sd, 0x00, 0xff); /* power down ADC */ + afe_write(sd, 0xc8, 0x40); /* phase control */ + + /* set to default gain for HDMI */ + cp_write(sd, 0x73, 0x10); + cp_write(sd, 0x74, 0x04); + cp_write(sd, 0x75, 0x01); + cp_write(sd, 0x76, 0x00); + + /* reset ADI recommended settings for digitizer */ + /* "ADV7842 Register Settings Recommendations + * (rev. 2.5, June 2010)" p. 17. */ + afe_write(sd, 0x12, 0xfb); /* ADC noise shaping filter controls */ + afe_write(sd, 0x0c, 0x0d); /* CP core gain controls */ + cp_write(sd, 0x3e, 0x80); /* CP core pre-gain control, + enable color control */ + /* CP coast control */ + cp_write(sd, 0xc3, 0x33); /* Component mode */ + + /* color space conversion, autodetect color space */ + io_write_and_or(sd, 0x02, 0x0f, 0xf0); + break; + + default: + v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", + __func__, state->mode); + break; + } +} + +static int adv7842_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7842_state *state = to_state(sd); + + v4l2_dbg(2, debug, sd, "%s: input %d\n", __func__, input); + + switch (input) { + case ADV7842_SELECT_HDMI_PORT_A: + /* TODO select HDMI_COMP or HDMI_GR */ + state->mode = ADV7842_MODE_HDMI; + state->vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P; + state->hdmi_port_a = true; + break; + case ADV7842_SELECT_HDMI_PORT_B: + /* TODO select HDMI_COMP or HDMI_GR */ + state->mode = ADV7842_MODE_HDMI; + state->vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P; + state->hdmi_port_a = false; + break; + case ADV7842_SELECT_VGA_COMP: + v4l2_info(sd, "%s: VGA component: todo\n", __func__); + case ADV7842_SELECT_VGA_RGB: + state->mode = ADV7842_MODE_RGB; + state->vid_std_select = ADV7842_RGB_VID_STD_AUTO_GRAPH_MODE; + break; + case ADV7842_SELECT_SDP_CVBS: + state->mode = ADV7842_MODE_SDP; + state->vid_std_select = ADV7842_SDP_VID_STD_CVBS_SD_4x1; + break; + case ADV7842_SELECT_SDP_YC: + state->mode = ADV7842_MODE_SDP; + state->vid_std_select = ADV7842_SDP_VID_STD_YC_SD4_x1; + break; + default: + return -EINVAL; + } + + disable_input(sd); + select_input(sd, state->vid_std_select); + enable_input(sd); + + v4l2_subdev_notify(sd, ADV7842_FMT_CHANGE, NULL); + + return 0; +} + +static int adv7842_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index) + return -EINVAL; + /* Good enough for now */ + *code = V4L2_MBUS_FMT_FIXED; + return 0; +} + +static int adv7842_g_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct adv7842_state *state = to_state(sd); + + fmt->width = state->timings.bt.width; + fmt->height = state->timings.bt.height; + fmt->code = V4L2_MBUS_FMT_FIXED; + fmt->field = V4L2_FIELD_NONE; + + if (state->mode == ADV7842_MODE_SDP) { + /* SPD block */ + if (!(sdp_read(sd, 0x5A) & 0x01)) + return -EINVAL; + fmt->width = 720; + /* valid signal */ + if (state->norm & V4L2_STD_525_60) + fmt->height = 480; + else + fmt->height = 576; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; + } + + if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { + fmt->colorspace = (state->timings.bt.height <= 576) ? + V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; + } + return 0; +} + +static void adv7842_irq_enable(struct v4l2_subdev *sd, bool enable) +{ + if (enable) { + /* Enable SSPD, STDI and CP locked/unlocked interrupts */ + io_write(sd, 0x46, 0x9c); + /* ESDP_50HZ_DET interrupt */ + io_write(sd, 0x5a, 0x10); + /* Enable CABLE_DET_A/B_ST (+5v) interrupt */ + io_write(sd, 0x73, 0x03); + /* Enable V_LOCKED and DE_REGEN_LCK interrupts */ + io_write(sd, 0x78, 0x03); + /* Enable SDP Standard Detection Change and SDP Video Detected */ + io_write(sd, 0xa0, 0x09); + } else { + io_write(sd, 0x46, 0x0); + io_write(sd, 0x5a, 0x0); + io_write(sd, 0x73, 0x0); + io_write(sd, 0x78, 0x0); + io_write(sd, 0xa0, 0x0); + } +} + +static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + struct adv7842_state *state = to_state(sd); + u8 fmt_change_cp, fmt_change_digital, fmt_change_sdp; + u8 irq_status[5]; + u8 irq_cfg = io_read(sd, 0x40); + + /* disable irq-pin output */ + io_write(sd, 0x40, irq_cfg | 0x3); + + /* read status */ + irq_status[0] = io_read(sd, 0x43); + irq_status[1] = io_read(sd, 0x57); + irq_status[2] = io_read(sd, 0x70); + irq_status[3] = io_read(sd, 0x75); + irq_status[4] = io_read(sd, 0x9d); + + /* and clear */ + if (irq_status[0]) + io_write(sd, 0x44, irq_status[0]); + if (irq_status[1]) + io_write(sd, 0x58, irq_status[1]); + if (irq_status[2]) + io_write(sd, 0x71, irq_status[2]); + if (irq_status[3]) + io_write(sd, 0x76, irq_status[3]); + if (irq_status[4]) + io_write(sd, 0x9e, irq_status[4]); + + v4l2_dbg(1, debug, sd, "%s: irq %x, %x, %x, %x, %x\n", __func__, + irq_status[0], irq_status[1], irq_status[2], + irq_status[3], irq_status[4]); + + /* format change CP */ + fmt_change_cp = irq_status[0] & 0x9c; + + /* format change SDP */ + if (state->mode == ADV7842_MODE_SDP) + fmt_change_sdp = (irq_status[1] & 0x30) | (irq_status[4] & 0x09); + else + fmt_change_sdp = 0; + + /* digital format CP */ + if (is_digital_input(sd)) + fmt_change_digital = irq_status[3] & 0x03; + else + fmt_change_digital = 0; + + /* notify */ + if (fmt_change_cp || fmt_change_digital || fmt_change_sdp) { + v4l2_dbg(1, debug, sd, + "%s: fmt_change_cp = 0x%x, fmt_change_digital = 0x%x, fmt_change_sdp = 0x%x\n", + __func__, fmt_change_cp, fmt_change_digital, + fmt_change_sdp); + v4l2_subdev_notify(sd, ADV7842_FMT_CHANGE, NULL); + } + + /* 5v cable detect */ + if (irq_status[2]) + adv7842_s_detect_tx_5v_ctrl(sd); + + if (handled) + *handled = true; + + /* re-enable irq-pin output */ + io_write(sd, 0x40, irq_cfg); + + return 0; +} + +static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *e) +{ + struct adv7842_state *state = to_state(sd); + int err = 0; + + if (e->pad > 2) + return -EINVAL; + if (e->start_block != 0) + return -EINVAL; + if (e->blocks > 2) + return -E2BIG; + if (!e->edid) + return -EINVAL; + + /* todo, per edid */ + state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15], + e->edid[0x16]); + + if (e->pad == 2) { + memset(&state->vga_edid.edid, 0, 256); + state->vga_edid.present = e->blocks ? 0x1 : 0x0; + memcpy(&state->vga_edid.edid, e->edid, 128 * e->blocks); + err = edid_write_vga_segment(sd); + } else { + u32 mask = 0x1<pad; + memset(&state->hdmi_edid.edid, 0, 256); + if (e->blocks) + state->hdmi_edid.present |= mask; + else + state->hdmi_edid.present &= ~mask; + memcpy(&state->hdmi_edid.edid, e->edid, 128*e->blocks); + err = edid_write_hdmi_segment(sd, e->pad); + } + if (err < 0) + v4l2_err(sd, "error %d writing edid on port %d\n", err, e->pad); + return err; +} + +/*********** avi info frame CEA-861-E **************/ +/* TODO move to common library */ + +struct avi_info_frame { + uint8_t f17; + uint8_t y10; + uint8_t a0; + uint8_t b10; + uint8_t s10; + uint8_t c10; + uint8_t m10; + uint8_t r3210; + uint8_t itc; + uint8_t ec210; + uint8_t q10; + uint8_t sc10; + uint8_t f47; + uint8_t vic; + uint8_t yq10; + uint8_t cn10; + uint8_t pr3210; + uint16_t etb; + uint16_t sbb; + uint16_t elb; + uint16_t srb; +}; + +static const char *y10_txt[4] = { + "RGB", + "YCbCr 4:2:2", + "YCbCr 4:4:4", + "Future", +}; + +static const char *c10_txt[4] = { + "No Data", + "SMPTE 170M", + "ITU-R 709", + "Extended Colorimetry information valied", +}; + +static const char *itc_txt[2] = { + "No Data", + "IT content", +}; + +static const char *ec210_txt[8] = { + "xvYCC601", + "xvYCC709", + "sYCC601", + "AdobeYCC601", + "AdobeRGB", + "5 reserved", + "6 reserved", + "7 reserved", +}; + +static const char *q10_txt[4] = { + "Default", + "Limited Range", + "Full Range", + "Reserved", +}; + +static void parse_avi_infoframe(struct v4l2_subdev *sd, uint8_t *buf, + struct avi_info_frame *avi) +{ + avi->f17 = (buf[1] >> 7) & 0x1; + avi->y10 = (buf[1] >> 5) & 0x3; + avi->a0 = (buf[1] >> 4) & 0x1; + avi->b10 = (buf[1] >> 2) & 0x3; + avi->s10 = buf[1] & 0x3; + avi->c10 = (buf[2] >> 6) & 0x3; + avi->m10 = (buf[2] >> 4) & 0x3; + avi->r3210 = buf[2] & 0xf; + avi->itc = (buf[3] >> 7) & 0x1; + avi->ec210 = (buf[3] >> 4) & 0x7; + avi->q10 = (buf[3] >> 2) & 0x3; + avi->sc10 = buf[3] & 0x3; + avi->f47 = (buf[4] >> 7) & 0x1; + avi->vic = buf[4] & 0x7f; + avi->yq10 = (buf[5] >> 6) & 0x3; + avi->cn10 = (buf[5] >> 4) & 0x3; + avi->pr3210 = buf[5] & 0xf; + avi->etb = buf[6] + 256*buf[7]; + avi->sbb = buf[8] + 256*buf[9]; + avi->elb = buf[10] + 256*buf[11]; + avi->srb = buf[12] + 256*buf[13]; +} + +static void print_avi_infoframe(struct v4l2_subdev *sd) +{ + int i; + uint8_t buf[14]; + uint8_t avi_inf_len; + struct avi_info_frame avi; + + if (!(hdmi_read(sd, 0x05) & 0x80)) { + v4l2_info(sd, "receive DVI-D signal (AVI infoframe not supported)\n"); + return; + } + if (!(io_read(sd, 0x60) & 0x01)) { + v4l2_info(sd, "AVI infoframe not received\n"); + return; + } + + if (io_read(sd, 0x88) & 0x10) { + /* Note: the ADV7842 calculated incorrect checksums for InfoFrames + with a length of 14 or 15. See the ADV7842 Register Settings + Recommendations document for more details. */ + v4l2_info(sd, "AVI infoframe checksum error\n"); + return; + } + + avi_inf_len = infoframe_read(sd, 0xe2); + v4l2_info(sd, "AVI infoframe version %d (%d byte)\n", + infoframe_read(sd, 0xe1), avi_inf_len); + + if (infoframe_read(sd, 0xe1) != 0x02) + return; + + for (i = 0; i < 14; i++) + buf[i] = infoframe_read(sd, i); + + v4l2_info(sd, "\t%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]); + + parse_avi_infoframe(sd, buf, &avi); + + if (avi.vic) + v4l2_info(sd, "\tVIC: %d\n", avi.vic); + if (avi.itc) + v4l2_info(sd, "\t%s\n", itc_txt[avi.itc]); + + if (avi.y10) + v4l2_info(sd, "\t%s %s\n", y10_txt[avi.y10], !avi.c10 ? "" : + (avi.c10 == 0x3 ? ec210_txt[avi.ec210] : c10_txt[avi.c10])); + else + v4l2_info(sd, "\t%s %s\n", y10_txt[avi.y10], q10_txt[avi.q10]); +} + +static const char * const prim_mode_txt[] = { + "SDP", + "Component", + "Graphics", + "Reserved", + "CVBS & HDMI AUDIO", + "HDMI-Comp", + "HDMI-GR", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", +}; + +static int adv7842_sdp_log_status(struct v4l2_subdev *sd) +{ + /* SDP (Standard definition processor) block */ + uint8_t sdp_signal_detected = sdp_read(sd, 0x5A) & 0x01; + + v4l2_info(sd, "Chip powered %s\n", no_power(sd) ? "off" : "on"); + v4l2_info(sd, "Prim-mode = 0x%x, video std = 0x%x\n", + io_read(sd, 0x01) & 0x0f, io_read(sd, 0x00) & 0x3f); + + v4l2_info(sd, "SDP: free run: %s\n", + (sdp_read(sd, 0x56) & 0x01) ? "on" : "off"); + v4l2_info(sd, "SDP: %s\n", sdp_signal_detected ? + "valid SD/PR signal detected" : "invalid/no signal"); + if (sdp_signal_detected) { + static const char * const sdp_std_txt[] = { + "NTSC-M/J", + "1?", + "NTSC-443", + "60HzSECAM", + "PAL-M", + "5?", + "PAL-60", + "7?", "8?", "9?", "a?", "b?", + "PAL-CombN", + "d?", + "PAL-BGHID", + "SECAM" + }; + v4l2_info(sd, "SDP: standard %s\n", + sdp_std_txt[sdp_read(sd, 0x52) & 0x0f]); + v4l2_info(sd, "SDP: %s\n", + (sdp_read(sd, 0x59) & 0x08) ? "50Hz" : "60Hz"); + v4l2_info(sd, "SDP: %s\n", + (sdp_read(sd, 0x57) & 0x08) ? "Interlaced" : "Progressive"); + v4l2_info(sd, "SDP: deinterlacer %s\n", + (sdp_read(sd, 0x12) & 0x08) ? "enabled" : "disabled"); + v4l2_info(sd, "SDP: csc %s mode\n", + (sdp_io_read(sd, 0xe0) & 0x40) ? "auto" : "manual"); + } + return 0; +} + +static int adv7842_cp_log_status(struct v4l2_subdev *sd) +{ + /* CP block */ + struct adv7842_state *state = to_state(sd); + struct v4l2_dv_timings timings; + uint8_t reg_io_0x02 = io_read(sd, 0x02); + uint8_t reg_io_0x21 = io_read(sd, 0x21); + uint8_t reg_rep_0x77 = rep_read(sd, 0x77); + uint8_t reg_rep_0x7d = rep_read(sd, 0x7d); + bool audio_pll_locked = hdmi_read(sd, 0x04) & 0x01; + bool audio_sample_packet_detect = hdmi_read(sd, 0x18) & 0x01; + bool audio_mute = io_read(sd, 0x65) & 0x40; + + static const char * const csc_coeff_sel_rb[16] = { + "bypassed", "YPbPr601 -> RGB", "reserved", "YPbPr709 -> RGB", + "reserved", "RGB -> YPbPr601", "reserved", "RGB -> YPbPr709", + "reserved", "YPbPr709 -> YPbPr601", "YPbPr601 -> YPbPr709", + "reserved", "reserved", "reserved", "reserved", "manual" + }; + static const char * const input_color_space_txt[16] = { + "RGB limited range (16-235)", "RGB full range (0-255)", + "YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", + "XvYCC Bt.601", "XvYCC Bt.709", + "YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)", + "invalid", "invalid", "invalid", "invalid", "invalid", + "invalid", "invalid", "automatic" + }; + static const char * const rgb_quantization_range_txt[] = { + "Automatic", + "RGB limited range (16-235)", + "RGB full range (0-255)", + }; + static const char * const deep_color_mode_txt[4] = { + "8-bits per channel", + "10-bits per channel", + "12-bits per channel", + "16-bits per channel (not supported)" + }; + + v4l2_info(sd, "-----Chip status-----\n"); + v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on"); + v4l2_info(sd, "Connector type: %s\n", state->connector_hdmi ? + "HDMI" : (is_digital_input(sd) ? "DVI-D" : "DVI-A")); + v4l2_info(sd, "HDMI/DVI-D port selected: %s\n", + state->hdmi_port_a ? "A" : "B"); + v4l2_info(sd, "EDID A %s, B %s\n", + ((reg_rep_0x7d & 0x04) && (reg_rep_0x77 & 0x04)) ? + "enabled" : "disabled", + ((reg_rep_0x7d & 0x08) && (reg_rep_0x77 & 0x08)) ? + "enabled" : "disabled"); + v4l2_info(sd, "HPD A %s, B %s\n", + reg_io_0x21 & 0x02 ? "enabled" : "disabled", + reg_io_0x21 & 0x01 ? "enabled" : "disabled"); + v4l2_info(sd, "CEC %s\n", !!(cec_read(sd, 0x2a) & 0x01) ? + "enabled" : "disabled"); + + v4l2_info(sd, "-----Signal status-----\n"); + if (state->hdmi_port_a) { + v4l2_info(sd, "Cable detected (+5V power): %s\n", + io_read(sd, 0x6f) & 0x02 ? "true" : "false"); + v4l2_info(sd, "TMDS signal detected: %s\n", + (io_read(sd, 0x6a) & 0x02) ? "true" : "false"); + v4l2_info(sd, "TMDS signal locked: %s\n", + (io_read(sd, 0x6a) & 0x20) ? "true" : "false"); + } else { + v4l2_info(sd, "Cable detected (+5V power):%s\n", + io_read(sd, 0x6f) & 0x01 ? "true" : "false"); + v4l2_info(sd, "TMDS signal detected: %s\n", + (io_read(sd, 0x6a) & 0x01) ? "true" : "false"); + v4l2_info(sd, "TMDS signal locked: %s\n", + (io_read(sd, 0x6a) & 0x10) ? "true" : "false"); + } + v4l2_info(sd, "CP free run: %s\n", + (!!(cp_read(sd, 0xff) & 0x10) ? "on" : "off")); + v4l2_info(sd, "Prim-mode = 0x%x, video std = 0x%x, v_freq = 0x%x\n", + io_read(sd, 0x01) & 0x0f, io_read(sd, 0x00) & 0x3f, + (io_read(sd, 0x01) & 0x70) >> 4); + + v4l2_info(sd, "-----Video Timings-----\n"); + if (no_cp_signal(sd)) { + v4l2_info(sd, "STDI: not locked\n"); + } else { + uint32_t bl = ((cp_read(sd, 0xb1) & 0x3f) << 8) | cp_read(sd, 0xb2); + uint32_t lcf = ((cp_read(sd, 0xb3) & 0x7) << 8) | cp_read(sd, 0xb4); + uint32_t lcvs = cp_read(sd, 0xb3) >> 3; + uint32_t fcl = ((cp_read(sd, 0xb8) & 0x1f) << 8) | cp_read(sd, 0xb9); + char hs_pol = ((cp_read(sd, 0xb5) & 0x10) ? + ((cp_read(sd, 0xb5) & 0x08) ? '+' : '-') : 'x'); + char vs_pol = ((cp_read(sd, 0xb5) & 0x40) ? + ((cp_read(sd, 0xb5) & 0x20) ? '+' : '-') : 'x'); + v4l2_info(sd, + "STDI: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, fcl = %d, %s, %chsync, %cvsync\n", + lcf, bl, lcvs, fcl, + (cp_read(sd, 0xb1) & 0x40) ? + "interlaced" : "progressive", + hs_pol, vs_pol); + } + if (adv7842_query_dv_timings(sd, &timings)) + v4l2_info(sd, "No video detected\n"); + else + v4l2_print_dv_timings(sd->name, "Detected format: ", + &timings, true); + v4l2_print_dv_timings(sd->name, "Configured format: ", + &state->timings, true); + + if (no_cp_signal(sd)) + return 0; + + v4l2_info(sd, "-----Color space-----\n"); + v4l2_info(sd, "RGB quantization range ctrl: %s\n", + rgb_quantization_range_txt[state->rgb_quantization_range]); + v4l2_info(sd, "Input color space: %s\n", + input_color_space_txt[reg_io_0x02 >> 4]); + v4l2_info(sd, "Output color space: %s %s, saturator %s\n", + (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr", + (reg_io_0x02 & 0x04) ? "(16-235)" : "(0-255)", + ((reg_io_0x02 & 0x04) ^ (reg_io_0x02 & 0x01)) ? + "enabled" : "disabled"); + v4l2_info(sd, "Color space conversion: %s\n", + csc_coeff_sel_rb[cp_read(sd, 0xf4) >> 4]); + + if (!is_digital_input(sd)) + return 0; + + v4l2_info(sd, "-----%s status-----\n", is_hdmi(sd) ? "HDMI" : "DVI-D"); + v4l2_info(sd, "HDCP encrypted content: %s\n", + (hdmi_read(sd, 0x05) & 0x40) ? "true" : "false"); + v4l2_info(sd, "HDCP keys read: %s%s\n", + (hdmi_read(sd, 0x04) & 0x20) ? "yes" : "no", + (hdmi_read(sd, 0x04) & 0x10) ? "ERROR" : ""); + if (!is_hdmi(sd)) + return 0; + + v4l2_info(sd, "Audio: pll %s, samples %s, %s\n", + audio_pll_locked ? "locked" : "not locked", + audio_sample_packet_detect ? "detected" : "not detected", + audio_mute ? "muted" : "enabled"); + if (audio_pll_locked && audio_sample_packet_detect) { + v4l2_info(sd, "Audio format: %s\n", + (hdmi_read(sd, 0x07) & 0x40) ? "multi-channel" : "stereo"); + } + v4l2_info(sd, "Audio CTS: %u\n", (hdmi_read(sd, 0x5b) << 12) + + (hdmi_read(sd, 0x5c) << 8) + + (hdmi_read(sd, 0x5d) & 0xf0)); + v4l2_info(sd, "Audio N: %u\n", ((hdmi_read(sd, 0x5d) & 0x0f) << 16) + + (hdmi_read(sd, 0x5e) << 8) + + hdmi_read(sd, 0x5f)); + v4l2_info(sd, "AV Mute: %s\n", + (hdmi_read(sd, 0x04) & 0x40) ? "on" : "off"); + v4l2_info(sd, "Deep color mode: %s\n", + deep_color_mode_txt[hdmi_read(sd, 0x0b) >> 6]); + + print_avi_infoframe(sd); + return 0; +} + +static int adv7842_log_status(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + + if (state->mode == ADV7842_MODE_SDP) + return adv7842_sdp_log_status(sd); + return adv7842_cp_log_status(sd); +} + +static int adv7842_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct adv7842_state *state = to_state(sd); + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + if (state->mode != ADV7842_MODE_SDP) + return -ENODATA; + + if (!(sdp_read(sd, 0x5A) & 0x01)) { + *std = 0; + v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); + return 0; + } + + switch (sdp_read(sd, 0x52) & 0x0f) { + case 0: + /* NTSC-M/J */ + *std &= V4L2_STD_NTSC; + break; + case 2: + /* NTSC-443 */ + *std &= V4L2_STD_NTSC_443; + break; + case 3: + /* 60HzSECAM */ + *std &= V4L2_STD_SECAM; + break; + case 4: + /* PAL-M */ + *std &= V4L2_STD_PAL_M; + break; + case 6: + /* PAL-60 */ + *std &= V4L2_STD_PAL_60; + break; + case 0xc: + /* PAL-CombN */ + *std &= V4L2_STD_PAL_Nc; + break; + case 0xe: + /* PAL-BGHID */ + *std &= V4L2_STD_PAL; + break; + case 0xf: + /* SECAM */ + *std &= V4L2_STD_SECAM; + break; + default: + *std &= V4L2_STD_ALL; + break; + } + return 0; +} + +static int adv7842_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct adv7842_state *state = to_state(sd); + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + if (state->mode != ADV7842_MODE_SDP) + return -ENODATA; + + if (norm & V4L2_STD_ALL) { + state->norm = norm; + return 0; + } + return -EINVAL; +} + +static int adv7842_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ + struct adv7842_state *state = to_state(sd); + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + if (state->mode != ADV7842_MODE_SDP) + return -ENODATA; + + *norm = state->norm; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int adv7842_core_init(struct v4l2_subdev *sd, + const struct adv7842_platform_data *pdata) +{ + hdmi_write(sd, 0x48, + (pdata->disable_pwrdnb ? 0x80 : 0) | + (pdata->disable_cable_det_rst ? 0x40 : 0)); + + disable_input(sd); + + /* power */ + io_write(sd, 0x0c, 0x42); /* Power up part and power down VDP */ + io_write(sd, 0x15, 0x80); /* Power up pads */ + + /* video format */ + io_write(sd, 0x02, + pdata->inp_color_space << 4 | + pdata->alt_gamma << 3 | + pdata->op_656_range << 2 | + pdata->rgb_out << 1 | + pdata->alt_data_sat << 0); + io_write(sd, 0x03, pdata->op_format_sel); + io_write_and_or(sd, 0x04, 0x1f, pdata->op_ch_sel << 5); + io_write_and_or(sd, 0x05, 0xf0, pdata->blank_data << 3 | + pdata->insert_av_codes << 2 | + pdata->replicate_av_codes << 1 | + pdata->invert_cbcr << 0); + + /* Drive strength */ + io_write_and_or(sd, 0x14, 0xc0, pdata->drive_strength.data<<4 | + pdata->drive_strength.clock<<2 | + pdata->drive_strength.sync); + + /* HDMI free run */ + cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01); + + /* TODO from platform data */ + cp_write(sd, 0x69, 0x14); /* Enable CP CSC */ + io_write(sd, 0x06, 0xa6); /* positive VS and HS and DE */ + cp_write(sd, 0xf3, 0xdc); /* Low threshold to enter/exit free run mode */ + afe_write(sd, 0xb5, 0x01); /* Setting MCLK to 256Fs */ + + afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */ + io_write_and_or(sd, 0x30, ~(1 << 4), pdata->output_bus_lsb_to_msb << 4); + + sdp_csc_coeff(sd, &pdata->sdp_csc_coeff); + + if (pdata->sdp_io_sync.adjust) { + const struct adv7842_sdp_io_sync_adjustment *s = &pdata->sdp_io_sync; + sdp_io_write(sd, 0x94, (s->hs_beg>>8) & 0xf); + sdp_io_write(sd, 0x95, s->hs_beg & 0xff); + sdp_io_write(sd, 0x96, (s->hs_width>>8) & 0xf); + sdp_io_write(sd, 0x97, s->hs_width & 0xff); + sdp_io_write(sd, 0x98, (s->de_beg>>8) & 0xf); + sdp_io_write(sd, 0x99, s->de_beg & 0xff); + sdp_io_write(sd, 0x9a, (s->de_end>>8) & 0xf); + sdp_io_write(sd, 0x9b, s->de_end & 0xff); + } + + /* todo, improve settings for sdram */ + if (pdata->sd_ram_size >= 128) { + sdp_write(sd, 0x12, 0x0d); /* Frame TBC,3D comb enabled */ + if (pdata->sd_ram_ddr) { + /* SDP setup for the AD eval board */ + sdp_io_write(sd, 0x6f, 0x00); /* DDR mode */ + sdp_io_write(sd, 0x75, 0x0a); /* 128 MB memory size */ + sdp_io_write(sd, 0x7a, 0xa5); /* Timing Adjustment */ + sdp_io_write(sd, 0x7b, 0x8f); /* Timing Adjustment */ + sdp_io_write(sd, 0x60, 0x01); /* SDRAM reset */ + } else { + sdp_io_write(sd, 0x75, 0x0a); /* 64 MB memory size ?*/ + sdp_io_write(sd, 0x74, 0x00); /* must be zero for sdr sdram */ + sdp_io_write(sd, 0x79, 0x33); /* CAS latency to 3, + depends on memory */ + sdp_io_write(sd, 0x6f, 0x01); /* SDR mode */ + sdp_io_write(sd, 0x7a, 0xa5); /* Timing Adjustment */ + sdp_io_write(sd, 0x7b, 0x8f); /* Timing Adjustment */ + sdp_io_write(sd, 0x60, 0x01); /* SDRAM reset */ + } + } else { + /* + * Manual UG-214, rev 0 is bit confusing on this bit + * but a '1' disables any signal if the Ram is active. + */ + sdp_io_write(sd, 0x29, 0x10); /* Tristate memory interface */ + } + + select_input(sd, pdata->vid_std_select); + + enable_input(sd); + + /* disable I2C access to internal EDID ram from HDMI DDC ports */ + rep_write_and_or(sd, 0x77, 0xf3, 0x00); + + hdmi_write(sd, 0x69, 0xa3); /* HPA manual */ + /* HPA disable on port A and B */ + io_write_and_or(sd, 0x20, 0xcf, 0x00); + + /* LLC */ + /* Set phase to 16. TODO: get this from platform_data */ + io_write(sd, 0x19, 0x90); + io_write(sd, 0x33, 0x40); + + /* interrupts */ + io_write(sd, 0x40, 0xe2); /* Configure INT1 */ + + adv7842_irq_enable(sd, true); + + return v4l2_ctrl_handler_setup(sd->ctrl_handler); +} + +/* ----------------------------------------------------------------------- */ + +static int adv7842_ddr_ram_test(struct v4l2_subdev *sd) +{ + /* + * From ADV784x external Memory test.pdf + * + * Reset must just been performed before running test. + * Recommended to reset after test. + */ + int i; + int pass = 0; + int fail = 0; + int complete = 0; + + io_write(sd, 0x00, 0x01); /* Program SDP 4x1 */ + io_write(sd, 0x01, 0x00); /* Program SDP mode */ + afe_write(sd, 0x80, 0x92); /* SDP Recommeneded Write */ + afe_write(sd, 0x9B, 0x01); /* SDP Recommeneded Write ADV7844ES1 */ + afe_write(sd, 0x9C, 0x60); /* SDP Recommeneded Write ADV7844ES1 */ + afe_write(sd, 0x9E, 0x02); /* SDP Recommeneded Write ADV7844ES1 */ + afe_write(sd, 0xA0, 0x0B); /* SDP Recommeneded Write ADV7844ES1 */ + afe_write(sd, 0xC3, 0x02); /* Memory BIST Initialisation */ + io_write(sd, 0x0C, 0x40); /* Power up ADV7844 */ + io_write(sd, 0x15, 0xBA); /* Enable outputs */ + sdp_write(sd, 0x12, 0x00); /* Disable 3D comb, Frame TBC & 3DNR */ + io_write(sd, 0xFF, 0x04); /* Reset memory controller */ + + mdelay(5); + + sdp_write(sd, 0x12, 0x00); /* Disable 3D Comb, Frame TBC & 3DNR */ + sdp_io_write(sd, 0x2A, 0x01); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x7c, 0x19); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x80, 0x87); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x81, 0x4a); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x82, 0x2c); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x83, 0x0e); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x84, 0x94); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x85, 0x62); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x7d, 0x00); /* Memory BIST Initialisation */ + sdp_io_write(sd, 0x7e, 0x1a); /* Memory BIST Initialisation */ + + mdelay(5); + + sdp_io_write(sd, 0xd9, 0xd5); /* Enable BIST Test */ + sdp_write(sd, 0x12, 0x05); /* Enable FRAME TBC & 3D COMB */ + + mdelay(20); + + for (i = 0; i < 10; i++) { + u8 result = sdp_io_read(sd, 0xdb); + if (result & 0x10) { + complete++; + if (result & 0x20) + fail++; + else + pass++; + } + mdelay(20); + } + + v4l2_dbg(1, debug, sd, + "Ram Test: completed %d of %d: pass %d, fail %d\n", + complete, i, pass, fail); + + if (!complete || fail) + return -EIO; + return 0; +} + +static void adv7842_rewrite_i2c_addresses(struct v4l2_subdev *sd, + struct adv7842_platform_data *pdata) +{ + io_write(sd, 0xf1, pdata->i2c_sdp << 1); + io_write(sd, 0xf2, pdata->i2c_sdp_io << 1); + io_write(sd, 0xf3, pdata->i2c_avlink << 1); + io_write(sd, 0xf4, pdata->i2c_cec << 1); + io_write(sd, 0xf5, pdata->i2c_infoframe << 1); + + io_write(sd, 0xf8, pdata->i2c_afe << 1); + io_write(sd, 0xf9, pdata->i2c_repeater << 1); + io_write(sd, 0xfa, pdata->i2c_edid << 1); + io_write(sd, 0xfb, pdata->i2c_hdmi << 1); + + io_write(sd, 0xfd, pdata->i2c_cp << 1); + io_write(sd, 0xfe, pdata->i2c_vdp << 1); +} + +static int adv7842_command_ram_test(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7842_state *state = to_state(sd); + struct adv7842_platform_data *pdata = client->dev.platform_data; + int ret = 0; + + if (!pdata) + return -ENODEV; + + if (!pdata->sd_ram_size || !pdata->sd_ram_ddr) { + v4l2_info(sd, "no sdram or no ddr sdram\n"); + return -EINVAL; + } + + main_reset(sd); + + adv7842_rewrite_i2c_addresses(sd, pdata); + + /* run ram test */ + ret = adv7842_ddr_ram_test(sd); + + main_reset(sd); + + adv7842_rewrite_i2c_addresses(sd, pdata); + + /* and re-init chip and state */ + adv7842_core_init(sd, pdata); + + disable_input(sd); + + select_input(sd, state->vid_std_select); + + enable_input(sd); + + adv7842_s_dv_timings(sd, &state->timings); + + edid_write_vga_segment(sd); + edid_write_hdmi_segment(sd, 0); + edid_write_hdmi_segment(sd, 1); + + return ret; +} + +static long adv7842_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + switch (cmd) { + case ADV7842_CMD_RAM_TEST: + return adv7842_command_ram_test(sd); + } + return -ENOTTY; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_ctrl_ops adv7842_ctrl_ops = { + .s_ctrl = adv7842_s_ctrl, +}; + +static const struct v4l2_subdev_core_ops adv7842_core_ops = { + .log_status = adv7842_log_status, + .g_std = adv7842_g_std, + .s_std = adv7842_s_std, + .ioctl = adv7842_ioctl, + .interrupt_service_routine = adv7842_isr, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = adv7842_g_register, + .s_register = adv7842_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops adv7842_video_ops = { + .s_routing = adv7842_s_routing, + .querystd = adv7842_querystd, + .g_input_status = adv7842_g_input_status, + .s_dv_timings = adv7842_s_dv_timings, + .g_dv_timings = adv7842_g_dv_timings, + .query_dv_timings = adv7842_query_dv_timings, + .enum_dv_timings = adv7842_enum_dv_timings, + .dv_timings_cap = adv7842_dv_timings_cap, + .enum_mbus_fmt = adv7842_enum_mbus_fmt, + .g_mbus_fmt = adv7842_g_mbus_fmt, + .try_mbus_fmt = adv7842_g_mbus_fmt, + .s_mbus_fmt = adv7842_g_mbus_fmt, +}; + +static const struct v4l2_subdev_pad_ops adv7842_pad_ops = { + .set_edid = adv7842_set_edid, +}; + +static const struct v4l2_subdev_ops adv7842_ops = { + .core = &adv7842_core_ops, + .video = &adv7842_video_ops, + .pad = &adv7842_pad_ops, +}; + +/* -------------------------- custom ctrls ---------------------------------- */ + +static const struct v4l2_ctrl_config adv7842_ctrl_analog_sampling_phase = { + .ops = &adv7842_ctrl_ops, + .id = V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE, + .name = "Analog Sampling Phase", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = 0, + .max = 0x1f, + .step = 1, + .def = 0, +}; + +static const struct v4l2_ctrl_config adv7842_ctrl_free_run_color_manual = { + .ops = &adv7842_ctrl_ops, + .id = V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL, + .name = "Free Running Color, Manual", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .max = 1, + .step = 1, + .def = 1, +}; + +static const struct v4l2_ctrl_config adv7842_ctrl_free_run_color = { + .ops = &adv7842_ctrl_ops, + .id = V4L2_CID_ADV_RX_FREE_RUN_COLOR, + .name = "Free Running Color", + .type = V4L2_CTRL_TYPE_INTEGER, + .max = 0xffffff, + .step = 0x1, +}; + + +static void adv7842_unregister_clients(struct adv7842_state *state) +{ + if (state->i2c_avlink) + i2c_unregister_device(state->i2c_avlink); + if (state->i2c_cec) + i2c_unregister_device(state->i2c_cec); + if (state->i2c_infoframe) + i2c_unregister_device(state->i2c_infoframe); + if (state->i2c_sdp_io) + i2c_unregister_device(state->i2c_sdp_io); + if (state->i2c_sdp) + i2c_unregister_device(state->i2c_sdp); + if (state->i2c_afe) + i2c_unregister_device(state->i2c_afe); + if (state->i2c_repeater) + i2c_unregister_device(state->i2c_repeater); + if (state->i2c_edid) + i2c_unregister_device(state->i2c_edid); + if (state->i2c_hdmi) + i2c_unregister_device(state->i2c_hdmi); + if (state->i2c_cp) + i2c_unregister_device(state->i2c_cp); + if (state->i2c_vdp) + i2c_unregister_device(state->i2c_vdp); +} + +static struct i2c_client *adv7842_dummy_client(struct v4l2_subdev *sd, + u8 addr, u8 io_reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + io_write(sd, io_reg, addr << 1); + return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); +} + +static int adv7842_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7842_state *state; + struct adv7842_platform_data *pdata = client->dev.platform_data; + struct v4l2_ctrl_handler *hdl; + struct v4l2_subdev *sd; + u16 rev; + int err; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_dbg(1, debug, client, "detecting adv7842 client on address 0x%x\n", + client->addr << 1); + + if (!pdata) { + v4l_err(client, "No platform data!\n"); + return -ENODEV; + } + + state = devm_kzalloc(&client->dev, sizeof(struct adv7842_state), GFP_KERNEL); + if (!state) { + v4l_err(client, "Could not allocate adv7842_state memory!\n"); + return -ENOMEM; + } + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &adv7842_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + state->connector_hdmi = pdata->connector_hdmi; + state->mode = pdata->mode; + + state->hdmi_port_a = true; + + /* i2c access to adv7842? */ + rev = adv_smbus_read_byte_data_check(client, 0xea, false) << 8 | + adv_smbus_read_byte_data_check(client, 0xeb, false); + if (rev != 0x2012) { + v4l2_info(sd, "got rev=0x%04x on first read attempt\n", rev); + rev = adv_smbus_read_byte_data_check(client, 0xea, false) << 8 | + adv_smbus_read_byte_data_check(client, 0xeb, false); + } + if (rev != 0x2012) { + v4l2_info(sd, "not an adv7842 on address 0x%x (rev=0x%04x)\n", + client->addr << 1, rev); + return -ENODEV; + } + + if (pdata->chip_reset) + main_reset(sd); + + /* control handlers */ + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 6); + + /* add in ascending ID order */ + v4l2_ctrl_new_std(hdl, &adv7842_ctrl_ops, + V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); + v4l2_ctrl_new_std(hdl, &adv7842_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &adv7842_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &adv7842_ctrl_ops, + V4L2_CID_HUE, 0, 128, 1, 0); + + /* custom controls */ + state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_DV_RX_POWER_PRESENT, 0, 3, 0, 0); + state->analog_sampling_phase_ctrl = v4l2_ctrl_new_custom(hdl, + &adv7842_ctrl_analog_sampling_phase, NULL); + state->free_run_color_ctrl_manual = v4l2_ctrl_new_custom(hdl, + &adv7842_ctrl_free_run_color_manual, NULL); + state->free_run_color_ctrl = v4l2_ctrl_new_custom(hdl, + &adv7842_ctrl_free_run_color, NULL); + state->rgb_quantization_range_ctrl = + v4l2_ctrl_new_std_menu(hdl, &adv7842_ctrl_ops, + V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, + 0, V4L2_DV_RGB_RANGE_AUTO); + sd->ctrl_handler = hdl; + if (hdl->error) { + err = hdl->error; + goto err_hdl; + } + state->detect_tx_5v_ctrl->is_private = true; + state->rgb_quantization_range_ctrl->is_private = true; + state->analog_sampling_phase_ctrl->is_private = true; + state->free_run_color_ctrl_manual->is_private = true; + state->free_run_color_ctrl->is_private = true; + + if (adv7842_s_detect_tx_5v_ctrl(sd)) { + err = -ENODEV; + goto err_hdl; + } + + state->i2c_avlink = adv7842_dummy_client(sd, pdata->i2c_avlink, 0xf3); + state->i2c_cec = adv7842_dummy_client(sd, pdata->i2c_cec, 0xf4); + state->i2c_infoframe = adv7842_dummy_client(sd, pdata->i2c_infoframe, 0xf5); + state->i2c_sdp_io = adv7842_dummy_client(sd, pdata->i2c_sdp_io, 0xf2); + state->i2c_sdp = adv7842_dummy_client(sd, pdata->i2c_sdp, 0xf1); + state->i2c_afe = adv7842_dummy_client(sd, pdata->i2c_afe, 0xf8); + state->i2c_repeater = adv7842_dummy_client(sd, pdata->i2c_repeater, 0xf9); + state->i2c_edid = adv7842_dummy_client(sd, pdata->i2c_edid, 0xfa); + state->i2c_hdmi = adv7842_dummy_client(sd, pdata->i2c_hdmi, 0xfb); + state->i2c_cp = adv7842_dummy_client(sd, pdata->i2c_cp, 0xfd); + state->i2c_vdp = adv7842_dummy_client(sd, pdata->i2c_vdp, 0xfe); + if (!state->i2c_avlink || !state->i2c_cec || !state->i2c_infoframe || + !state->i2c_sdp_io || !state->i2c_sdp || !state->i2c_afe || + !state->i2c_repeater || !state->i2c_edid || !state->i2c_hdmi || + !state->i2c_cp || !state->i2c_vdp) { + err = -ENOMEM; + v4l2_err(sd, "failed to create all i2c clients\n"); + goto err_i2c; + } + + /* work queues */ + state->work_queues = create_singlethread_workqueue(client->name); + if (!state->work_queues) { + v4l2_err(sd, "Could not create work queue\n"); + err = -ENOMEM; + goto err_i2c; + } + + INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, + adv7842_delayed_work_enable_hotplug); + + state->pad.flags = MEDIA_PAD_FL_SOURCE; + err = media_entity_init(&sd->entity, 1, &state->pad, 0); + if (err) + goto err_work_queues; + + err = adv7842_core_init(sd, pdata); + if (err) + goto err_entity; + + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, + client->addr << 1, client->adapter->name); + return 0; + +err_entity: + media_entity_cleanup(&sd->entity); +err_work_queues: + cancel_delayed_work(&state->delayed_work_enable_hotplug); + destroy_workqueue(state->work_queues); +err_i2c: + adv7842_unregister_clients(state); +err_hdl: + v4l2_ctrl_handler_free(hdl); + return err; +} + +/* ----------------------------------------------------------------------- */ + +static int adv7842_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7842_state *state = to_state(sd); + + adv7842_irq_enable(sd, false); + + cancel_delayed_work(&state->delayed_work_enable_hotplug); + destroy_workqueue(state->work_queues); + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + adv7842_unregister_clients(to_state(sd)); + v4l2_ctrl_handler_free(sd->ctrl_handler); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_device_id adv7842_id[] = { + { "adv7842", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adv7842_id); + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver adv7842_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7842", + }, + .probe = adv7842_probe, + .remove = adv7842_remove, + .id_table = adv7842_id, +}; + +module_i2c_driver(adv7842_driver); diff --git a/include/media/adv7842.h b/include/media/adv7842.h new file mode 100644 index 000000000000..c02201d1c092 --- /dev/null +++ b/include/media/adv7842.h @@ -0,0 +1,226 @@ +/* + * adv7842 - Analog Devices ADV7842 video decoder driver + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef _ADV7842_ +#define _ADV7842_ + +/* Analog input muxing modes (AFE register 0x02, [2:0]) */ +enum adv7842_ain_sel { + ADV7842_AIN1_2_3_NC_SYNC_1_2 = 0, + ADV7842_AIN4_5_6_NC_SYNC_2_1 = 1, + ADV7842_AIN7_8_9_NC_SYNC_3_1 = 2, + ADV7842_AIN10_11_12_NC_SYNC_4_1 = 3, + ADV7842_AIN9_4_5_6_SYNC_2_1 = 4, +}; + +/* Bus rotation and reordering (IO register 0x04, [7:5]) */ +enum adv7842_op_ch_sel { + ADV7842_OP_CH_SEL_GBR = 0, + ADV7842_OP_CH_SEL_GRB = 1, + ADV7842_OP_CH_SEL_BGR = 2, + ADV7842_OP_CH_SEL_RGB = 3, + ADV7842_OP_CH_SEL_BRG = 4, + ADV7842_OP_CH_SEL_RBG = 5, +}; + +/* Mode of operation */ +enum adv7842_mode { + ADV7842_MODE_SDP, + ADV7842_MODE_COMP, + ADV7842_MODE_RGB, + ADV7842_MODE_HDMI +}; + +/* Video standard select (IO register 0x00, [5:0]) */ +enum adv7842_vid_std_select { + /* SDP */ + ADV7842_SDP_VID_STD_CVBS_SD_4x1 = 0x01, + ADV7842_SDP_VID_STD_YC_SD4_x1 = 0x09, + /* RGB */ + ADV7842_RGB_VID_STD_AUTO_GRAPH_MODE = 0x07, + /* HDMI GR */ + ADV7842_HDMI_GR_VID_STD_AUTO_GRAPH_MODE = 0x02, + /* HDMI COMP */ + ADV7842_HDMI_COMP_VID_STD_HD_1250P = 0x1e, +}; + +/* Input Color Space (IO register 0x02, [7:4]) */ +enum adv7842_inp_color_space { + ADV7842_INP_COLOR_SPACE_LIM_RGB = 0, + ADV7842_INP_COLOR_SPACE_FULL_RGB = 1, + ADV7842_INP_COLOR_SPACE_LIM_YCbCr_601 = 2, + ADV7842_INP_COLOR_SPACE_LIM_YCbCr_709 = 3, + ADV7842_INP_COLOR_SPACE_XVYCC_601 = 4, + ADV7842_INP_COLOR_SPACE_XVYCC_709 = 5, + ADV7842_INP_COLOR_SPACE_FULL_YCbCr_601 = 6, + ADV7842_INP_COLOR_SPACE_FULL_YCbCr_709 = 7, + ADV7842_INP_COLOR_SPACE_AUTO = 0xf, +}; + +/* Select output format (IO register 0x03, [7:0]) */ +enum adv7842_op_format_sel { + ADV7842_OP_FORMAT_SEL_SDR_ITU656_8 = 0x00, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_10 = 0x01, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_12_MODE0 = 0x02, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_12_MODE1 = 0x06, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_12_MODE2 = 0x0a, + ADV7842_OP_FORMAT_SEL_DDR_422_8 = 0x20, + ADV7842_OP_FORMAT_SEL_DDR_422_10 = 0x21, + ADV7842_OP_FORMAT_SEL_DDR_422_12_MODE0 = 0x22, + ADV7842_OP_FORMAT_SEL_DDR_422_12_MODE1 = 0x23, + ADV7842_OP_FORMAT_SEL_DDR_422_12_MODE2 = 0x24, + ADV7842_OP_FORMAT_SEL_SDR_444_24 = 0x40, + ADV7842_OP_FORMAT_SEL_SDR_444_30 = 0x41, + ADV7842_OP_FORMAT_SEL_SDR_444_36_MODE0 = 0x42, + ADV7842_OP_FORMAT_SEL_DDR_444_24 = 0x60, + ADV7842_OP_FORMAT_SEL_DDR_444_30 = 0x61, + ADV7842_OP_FORMAT_SEL_DDR_444_36 = 0x62, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_16 = 0x80, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_20 = 0x81, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_24_MODE0 = 0x82, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_24_MODE1 = 0x86, + ADV7842_OP_FORMAT_SEL_SDR_ITU656_24_MODE2 = 0x8a, +}; + +enum adv7842_select_input { + ADV7842_SELECT_HDMI_PORT_A, + ADV7842_SELECT_HDMI_PORT_B, + ADV7842_SELECT_VGA_RGB, + ADV7842_SELECT_VGA_COMP, + ADV7842_SELECT_SDP_CVBS, + ADV7842_SELECT_SDP_YC, +}; + +struct adv7842_sdp_csc_coeff { + bool manual; + uint16_t scaling; + uint16_t A1; + uint16_t A2; + uint16_t A3; + uint16_t A4; + uint16_t B1; + uint16_t B2; + uint16_t B3; + uint16_t B4; + uint16_t C1; + uint16_t C2; + uint16_t C3; + uint16_t C4; +}; + +struct adv7842_sdp_io_sync_adjustment { + bool adjust; + uint16_t hs_beg; + uint16_t hs_width; + uint16_t de_beg; + uint16_t de_end; +}; + +/* Platform dependent definition */ +struct adv7842_platform_data { + /* connector - HDMI or DVI? */ + unsigned connector_hdmi:1; + + /* chip reset during probe */ + unsigned chip_reset:1; + + /* DIS_PWRDNB: 1 if the PWRDNB pin is unused and unconnected */ + unsigned disable_pwrdnb:1; + + /* DIS_CABLE_DET_RST: 1 if the 5V pins are unused and unconnected */ + unsigned disable_cable_det_rst:1; + + /* Analog input muxing mode */ + enum adv7842_ain_sel ain_sel; + + /* Bus rotation and reordering */ + enum adv7842_op_ch_sel op_ch_sel; + + /* Default mode */ + enum adv7842_mode mode; + + /* Video standard */ + enum adv7842_vid_std_select vid_std_select; + + /* Input Color Space */ + enum adv7842_inp_color_space inp_color_space; + + /* Select output format */ + enum adv7842_op_format_sel op_format_sel; + + /* IO register 0x02 */ + unsigned alt_gamma:1; + unsigned op_656_range:1; + unsigned rgb_out:1; + unsigned alt_data_sat:1; + + /* IO register 0x05 */ + unsigned blank_data:1; + unsigned insert_av_codes:1; + unsigned replicate_av_codes:1; + unsigned invert_cbcr:1; + + /* IO register 0x30 */ + unsigned output_bus_lsb_to_msb:1; + + /* IO register 0x14 */ + struct { + unsigned data:2; + unsigned clock:2; + unsigned sync:2; + } drive_strength; + + /* External RAM for 3-D comb or frame synchronizer */ + unsigned sd_ram_size; /* ram size in MB */ + unsigned sd_ram_ddr:1; /* ddr or sdr sdram */ + + /* Free run */ + unsigned hdmi_free_run_mode; + + struct adv7842_sdp_csc_coeff sdp_csc_coeff; + + struct adv7842_sdp_io_sync_adjustment sdp_io_sync; + + /* i2c addresses */ + u8 i2c_sdp_io; + u8 i2c_sdp; + u8 i2c_cp; + u8 i2c_vdp; + u8 i2c_afe; + u8 i2c_hdmi; + u8 i2c_repeater; + u8 i2c_edid; + u8 i2c_infoframe; + u8 i2c_cec; + u8 i2c_avlink; +}; + +#define V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE (V4L2_CID_DV_CLASS_BASE + 0x1000) +#define V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL (V4L2_CID_DV_CLASS_BASE + 0x1001) +#define V4L2_CID_ADV_RX_FREE_RUN_COLOR (V4L2_CID_DV_CLASS_BASE + 0x1002) + +/* notify events */ +#define ADV7842_FMT_CHANGE 1 + +/* custom ioctl, used to test the external RAM that's used by the + * deinterlacer. */ +#define ADV7842_CMD_RAM_TEST _IO('V', BASE_VIDIOC_PRIVATE) + +#endif -- cgit v1.2.3-71-gd317 From 5a544cce2177fe361ba539db9ddaf1eff4e73f81 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Aug 2013 09:12:36 -0300 Subject: [media] adv7511: add new video encoder This is an Analog Devices HDMI transmitter. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/Kconfig | 11 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/adv7511.c | 1198 +++++++++++++++++++++++++++++++++++++++++++ include/media/adv7511.h | 48 ++ 4 files changed, 1258 insertions(+) create mode 100644 drivers/media/i2c/adv7511.c create mode 100644 include/media/adv7511.h (limited to 'include') diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 847b7113f706..d18be19c96cd 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -429,6 +429,17 @@ config VIDEO_ADV7393 To compile this driver as a module, choose M here: the module will be called adv7393. +config VIDEO_ADV7511 + tristate "Analog Devices ADV7511 encoder" + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + ---help--- + Support for the Analog Devices ADV7511 video encoder. + + This is a Analog Devices HDMI transmitter. + + To compile this driver as a module, choose M here: the + module will be called adv7511. + config VIDEO_AD9389B tristate "Analog Devices AD9389B encoder" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index b4cf972703d2..9f462df77b4a 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o obj-$(CONFIG_VIDEO_ADV7842) += adv7842.o obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o +obj-$(CONFIG_VIDEO_ADV7511) += adv7511.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_VS6624) += vs6624.o obj-$(CONFIG_VIDEO_BT819) += bt819.o diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c new file mode 100644 index 000000000000..7a576097471f --- /dev/null +++ b/drivers/media/i2c/adv7511.c @@ -0,0 +1,1198 @@ +/* + * Analog Devices ADV7511 HDMI Transmitter Device Driver + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-2)"); + +MODULE_DESCRIPTION("Analog Devices ADV7511 HDMI Transmitter Device Driver"); +MODULE_AUTHOR("Hans Verkuil"); +MODULE_LICENSE("GPL"); + +#define MASK_ADV7511_EDID_RDY_INT 0x04 +#define MASK_ADV7511_MSEN_INT 0x40 +#define MASK_ADV7511_HPD_INT 0x80 + +#define MASK_ADV7511_HPD_DETECT 0x40 +#define MASK_ADV7511_MSEN_DETECT 0x20 +#define MASK_ADV7511_EDID_RDY 0x10 + +#define EDID_MAX_RETRIES (8) +#define EDID_DELAY 250 +#define EDID_MAX_SEGM 8 + +#define ADV7511_MAX_WIDTH 1920 +#define ADV7511_MAX_HEIGHT 1200 +#define ADV7511_MIN_PIXELCLOCK 20000000 +#define ADV7511_MAX_PIXELCLOCK 225000000 + +/* +********************************************************************** +* +* Arrays with configuration parameters for the ADV7511 +* +********************************************************************** +*/ + +struct i2c_reg_value { + unsigned char reg; + unsigned char value; +}; + +struct adv7511_state_edid { + /* total number of blocks */ + u32 blocks; + /* Number of segments read */ + u32 segments; + uint8_t data[EDID_MAX_SEGM * 256]; + /* Number of EDID read retries left */ + unsigned read_retries; + bool complete; +}; + +struct adv7511_state { + struct adv7511_platform_data pdata; + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_ctrl_handler hdl; + int chip_revision; + uint8_t i2c_edid_addr; + uint8_t i2c_cec_addr; + /* Is the adv7511 powered on? */ + bool power_on; + /* Did we receive hotplug and rx-sense signals? */ + bool have_monitor; + /* timings from s_dv_timings */ + struct v4l2_dv_timings dv_timings; + /* controls */ + struct v4l2_ctrl *hdmi_mode_ctrl; + struct v4l2_ctrl *hotplug_ctrl; + struct v4l2_ctrl *rx_sense_ctrl; + struct v4l2_ctrl *have_edid0_ctrl; + struct v4l2_ctrl *rgb_quantization_range_ctrl; + struct i2c_client *i2c_edid; + struct adv7511_state_edid edid; + /* Running counter of the number of detected EDIDs (for debugging) */ + unsigned edid_detect_counter; + struct workqueue_struct *work_queue; + struct delayed_work edid_handler; /* work entry */ +}; + +static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd); +static bool adv7511_check_edid_status(struct v4l2_subdev *sd); +static void adv7511_setup(struct v4l2_subdev *sd); +static int adv7511_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq); +static int adv7511_s_clock_freq(struct v4l2_subdev *sd, u32 freq); + + +static const struct v4l2_dv_timings_cap adv7511_timings_cap = { + .type = V4L2_DV_BT_656_1120, + .bt = { + .max_width = ADV7511_MAX_WIDTH, + .max_height = ADV7511_MAX_HEIGHT, + .min_pixelclock = ADV7511_MIN_PIXELCLOCK, + .max_pixelclock = ADV7511_MAX_PIXELCLOCK, + .standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | + V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, + .capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | + V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM, + }, +}; + +static inline struct adv7511_state *get_adv7511_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7511_state, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct adv7511_state, hdl)->sd; +} + +/* ------------------------ I2C ----------------------------------------------- */ + +static s32 adv_smbus_read_byte_data_check(struct i2c_client *client, + u8 command, bool check) +{ + union i2c_smbus_data data; + + if (!i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data)) + return data.byte; + if (check) + v4l_err(client, "error reading %02x, %02x\n", + client->addr, command); + return -1; +} + +static s32 adv_smbus_read_byte_data(struct i2c_client *client, u8 command) +{ + int i; + for (i = 0; i < 3; i++) { + int ret = adv_smbus_read_byte_data_check(client, command, true); + if (ret >= 0) { + if (i) + v4l_err(client, "read ok after %d retries\n", i); + return ret; + } + } + v4l_err(client, "read failed\n"); + return -1; +} + +static int adv7511_rd(struct v4l2_subdev *sd, u8 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return adv_smbus_read_byte_data(client, reg); +} + +static int adv7511_wr(struct v4l2_subdev *sd, u8 reg, u8 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + int i; + + for (i = 0; i < 3; i++) { + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret == 0) + return 0; + } + v4l2_err(sd, "%s: i2c write error\n", __func__); + return ret; +} + +/* To set specific bits in the register, a clear-mask is given (to be AND-ed), + and then the value-mask (to be OR-ed). */ +static inline void adv7511_wr_and_or(struct v4l2_subdev *sd, u8 reg, uint8_t clr_mask, uint8_t val_mask) +{ + adv7511_wr(sd, reg, (adv7511_rd(sd, reg) & clr_mask) | val_mask); +} + +static int adv_smbus_read_i2c_block_data(struct i2c_client *client, + u8 command, unsigned length, u8 *values) +{ + union i2c_smbus_data data; + int ret; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + + ret = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); + memcpy(values, data.block + 1, length); + return ret; +} + +static inline void adv7511_edid_rd(struct v4l2_subdev *sd, uint16_t len, uint8_t *buf) +{ + struct adv7511_state *state = get_adv7511_state(sd); + int i; + int err = 0; + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX) + err = adv_smbus_read_i2c_block_data(state->i2c_edid, i, + I2C_SMBUS_BLOCK_MAX, buf + i); + if (err) + v4l2_err(sd, "%s: i2c read error\n", __func__); +} + +static inline bool adv7511_have_hotplug(struct v4l2_subdev *sd) +{ + return adv7511_rd(sd, 0x42) & MASK_ADV7511_HPD_DETECT; +} + +static inline bool adv7511_have_rx_sense(struct v4l2_subdev *sd) +{ + return adv7511_rd(sd, 0x42) & MASK_ADV7511_MSEN_DETECT; +} + +static void adv7511_csc_conversion_mode(struct v4l2_subdev *sd, uint8_t mode) +{ + adv7511_wr_and_or(sd, 0x18, 0x9f, (mode & 0x3)<<5); +} + +static void adv7511_csc_coeff(struct v4l2_subdev *sd, + u16 A1, u16 A2, u16 A3, u16 A4, + u16 B1, u16 B2, u16 B3, u16 B4, + u16 C1, u16 C2, u16 C3, u16 C4) +{ + /* A */ + adv7511_wr_and_or(sd, 0x18, 0xe0, A1>>8); + adv7511_wr(sd, 0x19, A1); + adv7511_wr_and_or(sd, 0x1A, 0xe0, A2>>8); + adv7511_wr(sd, 0x1B, A2); + adv7511_wr_and_or(sd, 0x1c, 0xe0, A3>>8); + adv7511_wr(sd, 0x1d, A3); + adv7511_wr_and_or(sd, 0x1e, 0xe0, A4>>8); + adv7511_wr(sd, 0x1f, A4); + + /* B */ + adv7511_wr_and_or(sd, 0x20, 0xe0, B1>>8); + adv7511_wr(sd, 0x21, B1); + adv7511_wr_and_or(sd, 0x22, 0xe0, B2>>8); + adv7511_wr(sd, 0x23, B2); + adv7511_wr_and_or(sd, 0x24, 0xe0, B3>>8); + adv7511_wr(sd, 0x25, B3); + adv7511_wr_and_or(sd, 0x26, 0xe0, B4>>8); + adv7511_wr(sd, 0x27, B4); + + /* C */ + adv7511_wr_and_or(sd, 0x28, 0xe0, C1>>8); + adv7511_wr(sd, 0x29, C1); + adv7511_wr_and_or(sd, 0x2A, 0xe0, C2>>8); + adv7511_wr(sd, 0x2B, C2); + adv7511_wr_and_or(sd, 0x2C, 0xe0, C3>>8); + adv7511_wr(sd, 0x2D, C3); + adv7511_wr_and_or(sd, 0x2E, 0xe0, C4>>8); + adv7511_wr(sd, 0x2F, C4); +} + +static void adv7511_csc_rgb_full2limit(struct v4l2_subdev *sd, bool enable) +{ + if (enable) { + uint8_t csc_mode = 0; + adv7511_csc_conversion_mode(sd, csc_mode); + adv7511_csc_coeff(sd, + 4096-564, 0, 0, 256, + 0, 4096-564, 0, 256, + 0, 0, 4096-564, 256); + /* enable CSC */ + adv7511_wr_and_or(sd, 0x18, 0x7f, 0x80); + /* AVI infoframe: Limited range RGB (16-235) */ + adv7511_wr_and_or(sd, 0x57, 0xf3, 0x04); + } else { + /* disable CSC */ + adv7511_wr_and_or(sd, 0x18, 0x7f, 0x0); + /* AVI infoframe: Full range RGB (0-255) */ + adv7511_wr_and_or(sd, 0x57, 0xf3, 0x08); + } +} + +static void adv7511_set_IT_content_AVI_InfoFrame(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + if (state->dv_timings.bt.standards & V4L2_DV_BT_STD_CEA861) { + /* CEA format, not IT */ + adv7511_wr_and_or(sd, 0x57, 0x7f, 0x00); + } else { + /* IT format */ + adv7511_wr_and_or(sd, 0x57, 0x7f, 0x80); + } +} + +static int adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2_ctrl *ctrl) +{ + switch (ctrl->val) { + default: + return -EINVAL; + break; + case V4L2_DV_RGB_RANGE_AUTO: { + /* automatic */ + struct adv7511_state *state = get_adv7511_state(sd); + + if (state->dv_timings.bt.standards & V4L2_DV_BT_STD_CEA861) { + /* cea format, RGB limited range (16-235) */ + adv7511_csc_rgb_full2limit(sd, true); + } else { + /* not cea format, RGB full range (0-255) */ + adv7511_csc_rgb_full2limit(sd, false); + } + } + break; + case V4L2_DV_RGB_RANGE_LIMITED: + /* RGB limited range (16-235) */ + adv7511_csc_rgb_full2limit(sd, true); + break; + case V4L2_DV_RGB_RANGE_FULL: + /* RGB full range (0-255) */ + adv7511_csc_rgb_full2limit(sd, false); + break; + } + return 0; +} + +/* ------------------------------ CTRL OPS ------------------------------ */ + +static int adv7511_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = to_sd(ctrl); + struct adv7511_state *state = get_adv7511_state(sd); + + v4l2_dbg(1, debug, sd, "%s: ctrl id: %d, ctrl->val %d\n", __func__, ctrl->id, ctrl->val); + + if (state->hdmi_mode_ctrl == ctrl) { + /* Set HDMI or DVI-D */ + adv7511_wr_and_or(sd, 0xaf, 0xfd, ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00); + return 0; + } + if (state->rgb_quantization_range_ctrl == ctrl) + return adv7511_set_rgb_quantization_mode(sd, ctrl); + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops adv7511_ctrl_ops = { + .s_ctrl = adv7511_s_ctrl, +}; + +/* ---------------------------- CORE OPS ------------------------------------------- */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static void adv7511_inv_register(struct v4l2_subdev *sd) +{ + v4l2_info(sd, "0x000-0x0ff: Main Map\n"); +} + +static int adv7511_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + reg->size = 1; + switch (reg->reg >> 8) { + case 0: + reg->val = adv7511_rd(sd, reg->reg & 0xff); + break; + default: + v4l2_info(sd, "Register %03llx not supported\n", reg->reg); + adv7511_inv_register(sd); + break; + } + return 0; +} + +static int adv7511_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) +{ + switch (reg->reg >> 8) { + case 0: + adv7511_wr(sd, reg->reg & 0xff, reg->val & 0xff); + break; + default: + v4l2_info(sd, "Register %03llx not supported\n", reg->reg); + adv7511_inv_register(sd); + break; + } + return 0; +} +#endif + +static int adv7511_log_status(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + struct adv7511_state_edid *edid = &state->edid; + + static const char * const states[] = { + "in reset", + "reading EDID", + "idle", + "initializing HDCP", + "HDCP enabled", + "initializing HDCP repeater", + "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" + }; + static const char * const errors[] = { + "no error", + "bad receiver BKSV", + "Ri mismatch", + "Pj mismatch", + "i2c error", + "timed out", + "max repeater cascade exceeded", + "hash check failed", + "too many devices", + "9", "A", "B", "C", "D", "E", "F" + }; + + v4l2_info(sd, "power %s\n", state->power_on ? "on" : "off"); + v4l2_info(sd, "%s hotplug, %s Rx Sense, %s EDID (%d block(s))\n", + (adv7511_rd(sd, 0x42) & MASK_ADV7511_HPD_DETECT) ? "detected" : "no", + (adv7511_rd(sd, 0x42) & MASK_ADV7511_MSEN_DETECT) ? "detected" : "no", + edid->segments ? "found" : "no", + edid->blocks); + v4l2_info(sd, "%s output %s\n", + (adv7511_rd(sd, 0xaf) & 0x02) ? + "HDMI" : "DVI-D", + (adv7511_rd(sd, 0xa1) & 0x3c) ? + "disabled" : "enabled"); + v4l2_info(sd, "state: %s, error: %s, detect count: %u, msk/irq: %02x/%02x\n", + states[adv7511_rd(sd, 0xc8) & 0xf], + errors[adv7511_rd(sd, 0xc8) >> 4], state->edid_detect_counter, + adv7511_rd(sd, 0x94), adv7511_rd(sd, 0x96)); + v4l2_info(sd, "RGB quantization: %s range\n", adv7511_rd(sd, 0x18) & 0x80 ? "limited" : "full"); + if (state->dv_timings.type == V4L2_DV_BT_656_1120) + v4l2_print_dv_timings(sd->name, "timings: ", + &state->dv_timings, false); + else + v4l2_info(sd, "no timings set\n"); + v4l2_info(sd, "i2c edid addr: 0x%x\n", state->i2c_edid_addr); + v4l2_info(sd, "i2c cec addr: 0x%x\n", state->i2c_cec_addr); + return 0; +} + +/* Power up/down adv7511 */ +static int adv7511_s_power(struct v4l2_subdev *sd, int on) +{ + struct adv7511_state *state = get_adv7511_state(sd); + const int retries = 20; + int i; + + v4l2_dbg(1, debug, sd, "%s: power %s\n", __func__, on ? "on" : "off"); + + state->power_on = on; + + if (!on) { + /* Power down */ + adv7511_wr_and_or(sd, 0x41, 0xbf, 0x40); + return true; + } + + /* Power up */ + /* The adv7511 does not always come up immediately. + Retry multiple times. */ + for (i = 0; i < retries; i++) { + adv7511_wr_and_or(sd, 0x41, 0xbf, 0x0); + if ((adv7511_rd(sd, 0x41) & 0x40) == 0) + break; + adv7511_wr_and_or(sd, 0x41, 0xbf, 0x40); + msleep(10); + } + if (i == retries) { + v4l2_dbg(1, debug, sd, "%s: failed to powerup the adv7511!\n", __func__); + adv7511_s_power(sd, 0); + return false; + } + if (i > 1) + v4l2_dbg(1, debug, sd, "%s: needed %d retries to powerup the adv7511\n", __func__, i); + + /* Reserved registers that must be set */ + adv7511_wr(sd, 0x98, 0x03); + adv7511_wr_and_or(sd, 0x9a, 0xfe, 0x70); + adv7511_wr(sd, 0x9c, 0x30); + adv7511_wr_and_or(sd, 0x9d, 0xfc, 0x01); + adv7511_wr(sd, 0xa2, 0xa4); + adv7511_wr(sd, 0xa3, 0xa4); + adv7511_wr(sd, 0xe0, 0xd0); + adv7511_wr(sd, 0xf9, 0x00); + + adv7511_wr(sd, 0x43, state->i2c_edid_addr); + + /* Set number of attempts to read the EDID */ + adv7511_wr(sd, 0xc9, 0xf); + return true; +} + +/* Enable interrupts */ +static void adv7511_set_isr(struct v4l2_subdev *sd, bool enable) +{ + uint8_t irqs = MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT; + uint8_t irqs_rd; + int retries = 100; + + v4l2_dbg(2, debug, sd, "%s: %s\n", __func__, enable ? "enable" : "disable"); + + /* The datasheet says that the EDID ready interrupt should be + disabled if there is no hotplug. */ + if (!enable) + irqs = 0; + else if (adv7511_have_hotplug(sd)) + irqs |= MASK_ADV7511_EDID_RDY_INT; + + /* + * This i2c write can fail (approx. 1 in 1000 writes). But it + * is essential that this register is correct, so retry it + * multiple times. + * + * Note that the i2c write does not report an error, but the readback + * clearly shows the wrong value. + */ + do { + adv7511_wr(sd, 0x94, irqs); + irqs_rd = adv7511_rd(sd, 0x94); + } while (retries-- && irqs_rd != irqs); + + if (irqs_rd == irqs) + return; + v4l2_err(sd, "Could not set interrupts: hw failure?\n"); +} + +/* Interrupt handler */ +static int adv7511_isr(struct v4l2_subdev *sd, u32 status, bool *handled) +{ + uint8_t irq_status; + + /* disable interrupts to prevent a race condition */ + adv7511_set_isr(sd, false); + irq_status = adv7511_rd(sd, 0x96); + /* clear detected interrupts */ + adv7511_wr(sd, 0x96, irq_status); + + v4l2_dbg(1, debug, sd, "%s: irq 0x%x\n", __func__, irq_status); + + if (irq_status & (MASK_ADV7511_HPD_INT | MASK_ADV7511_MSEN_INT)) + adv7511_check_monitor_present_status(sd); + if (irq_status & MASK_ADV7511_EDID_RDY_INT) + adv7511_check_edid_status(sd); + + /* enable interrupts */ + adv7511_set_isr(sd, true); + + if (handled) + *handled = true; + return 0; +} + +static int adv7511_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + if (edid->pad != 0) + return -EINVAL; + if ((edid->blocks == 0) || (edid->blocks > 256)) + return -EINVAL; + if (!edid->edid) + return -EINVAL; + if (!state->edid.segments) { + v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n"); + return -ENODATA; + } + if (edid->start_block >= state->edid.segments * 2) + return -E2BIG; + if ((edid->blocks + edid->start_block) >= state->edid.segments * 2) + edid->blocks = state->edid.segments * 2 - edid->start_block; + + memcpy(edid->edid, &state->edid.data[edid->start_block * 128], + 128 * edid->blocks); + return 0; +} + +static const struct v4l2_subdev_pad_ops adv7511_pad_ops = { + .get_edid = adv7511_get_edid, +}; + +static const struct v4l2_subdev_core_ops adv7511_core_ops = { + .log_status = adv7511_log_status, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = adv7511_g_register, + .s_register = adv7511_s_register, +#endif + .s_power = adv7511_s_power, + .interrupt_service_routine = adv7511_isr, +}; + +/* ------------------------------ VIDEO OPS ------------------------------ */ + +/* Enable/disable adv7511 output */ +static int adv7511_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); + adv7511_wr_and_or(sd, 0xa1, ~0x3c, (enable ? 0 : 0x3c)); + if (enable) { + adv7511_check_monitor_present_status(sd); + } else { + adv7511_s_power(sd, 0); + state->have_monitor = false; + } + return 0; +} + +static int adv7511_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + /* quick sanity check */ + if (!v4l2_valid_dv_timings(timings, &adv7511_timings_cap, NULL, NULL)) + return -EINVAL; + + /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings + if the format is one of the CEA or DMT timings. */ + v4l2_find_dv_timings_cap(timings, &adv7511_timings_cap, 0, NULL, NULL); + + timings->bt.flags &= ~V4L2_DV_FL_REDUCED_FPS; + + /* save timings */ + state->dv_timings = *timings; + + /* update quantization range based on new dv_timings */ + adv7511_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl); + + /* update AVI infoframe */ + adv7511_set_IT_content_AVI_InfoFrame(sd); + + return 0; +} + +static int adv7511_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + struct adv7511_state *state = get_adv7511_state(sd); + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + if (!timings) + return -EINVAL; + + *timings = state->dv_timings; + + return 0; +} + +static int adv7511_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + return v4l2_enum_dv_timings_cap(timings, &adv7511_timings_cap, NULL, NULL); +} + +static int adv7511_dv_timings_cap(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap) +{ + *cap = adv7511_timings_cap; + return 0; +} + +static const struct v4l2_subdev_video_ops adv7511_video_ops = { + .s_stream = adv7511_s_stream, + .s_dv_timings = adv7511_s_dv_timings, + .g_dv_timings = adv7511_g_dv_timings, + .enum_dv_timings = adv7511_enum_dv_timings, + .dv_timings_cap = adv7511_dv_timings_cap, +}; + +/* ------------------------------ AUDIO OPS ------------------------------ */ +static int adv7511_s_audio_stream(struct v4l2_subdev *sd, int enable) +{ + v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); + + if (enable) + adv7511_wr_and_or(sd, 0x4b, 0x3f, 0x80); + else + adv7511_wr_and_or(sd, 0x4b, 0x3f, 0x40); + + return 0; +} + +static int adv7511_s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + u32 N; + + switch (freq) { + case 32000: N = 4096; break; + case 44100: N = 6272; break; + case 48000: N = 6144; break; + case 88200: N = 12544; break; + case 96000: N = 12288; break; + case 176400: N = 25088; break; + case 192000: N = 24576; break; + default: + return -EINVAL; + } + + /* Set N (used with CTS to regenerate the audio clock) */ + adv7511_wr(sd, 0x01, (N >> 16) & 0xf); + adv7511_wr(sd, 0x02, (N >> 8) & 0xff); + adv7511_wr(sd, 0x03, N & 0xff); + + return 0; +} + +static int adv7511_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + u32 i2s_sf; + + switch (freq) { + case 32000: i2s_sf = 0x30; break; + case 44100: i2s_sf = 0x00; break; + case 48000: i2s_sf = 0x20; break; + case 88200: i2s_sf = 0x80; break; + case 96000: i2s_sf = 0xa0; break; + case 176400: i2s_sf = 0xc0; break; + case 192000: i2s_sf = 0xe0; break; + default: + return -EINVAL; + } + + /* Set sampling frequency for I2S audio to 48 kHz */ + adv7511_wr_and_or(sd, 0x15, 0xf, i2s_sf); + + return 0; +} + +static int adv7511_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) +{ + /* Only 2 channels in use for application */ + adv7511_wr_and_or(sd, 0x73, 0xf8, 0x1); + /* Speaker mapping */ + adv7511_wr(sd, 0x76, 0x00); + + /* 16 bit audio word length */ + adv7511_wr_and_or(sd, 0x14, 0xf0, 0x02); + + return 0; +} + +static const struct v4l2_subdev_audio_ops adv7511_audio_ops = { + .s_stream = adv7511_s_audio_stream, + .s_clock_freq = adv7511_s_clock_freq, + .s_i2s_clock_freq = adv7511_s_i2s_clock_freq, + .s_routing = adv7511_s_routing, +}; + +/* --------------------- SUBDEV OPS --------------------------------------- */ + +static const struct v4l2_subdev_ops adv7511_ops = { + .core = &adv7511_core_ops, + .pad = &adv7511_pad_ops, + .video = &adv7511_video_ops, + .audio = &adv7511_audio_ops, +}; + +/* ----------------------------------------------------------------------- */ +static void adv7511_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd, int segment, uint8_t *buf) +{ + if (debug >= lvl) { + int i, j; + v4l2_dbg(lvl, debug, sd, "edid segment %d\n", segment); + for (i = 0; i < 256; i += 16) { + u8 b[128]; + u8 *bp = b; + if (i == 128) + v4l2_dbg(lvl, debug, sd, "\n"); + for (j = i; j < i + 16; j++) { + sprintf(bp, "0x%02x, ", buf[j]); + bp += 6; + } + bp[0] = '\0'; + v4l2_dbg(lvl, debug, sd, "%s\n", b); + } + } +} + +static void adv7511_edid_handler(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct adv7511_state *state = container_of(dwork, struct adv7511_state, edid_handler); + struct v4l2_subdev *sd = &state->sd; + struct adv7511_edid_detect ed; + + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + + if (adv7511_check_edid_status(sd)) { + /* Return if we received the EDID. */ + return; + } + + if (adv7511_have_hotplug(sd)) { + /* We must retry reading the EDID several times, it is possible + * that initially the EDID couldn't be read due to i2c errors + * (DVI connectors are particularly prone to this problem). */ + if (state->edid.read_retries) { + state->edid.read_retries--; + v4l2_dbg(1, debug, sd, "%s: edid read failed\n", __func__); + state->have_monitor = false; + adv7511_s_power(sd, false); + adv7511_s_power(sd, true); + queue_delayed_work(state->work_queue, &state->edid_handler, EDID_DELAY); + return; + } + } + + /* We failed to read the EDID, so send an event for this. */ + ed.present = false; + ed.segment = adv7511_rd(sd, 0xc4); + v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); + v4l2_dbg(1, debug, sd, "%s: no edid found\n", __func__); +} + +static void adv7511_audio_setup(struct v4l2_subdev *sd) +{ + v4l2_dbg(1, debug, sd, "%s\n", __func__); + + adv7511_s_i2s_clock_freq(sd, 48000); + adv7511_s_clock_freq(sd, 48000); + adv7511_s_routing(sd, 0, 0, 0); +} + +/* Configure hdmi transmitter. */ +static void adv7511_setup(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + v4l2_dbg(1, debug, sd, "%s\n", __func__); + + /* Input format: RGB 4:4:4 */ + adv7511_wr_and_or(sd, 0x15, 0xf0, 0x0); + /* Output format: RGB 4:4:4 */ + adv7511_wr_and_or(sd, 0x16, 0x7f, 0x0); + /* 1st order interpolation 4:2:2 -> 4:4:4 up conversion, Aspect ratio: 16:9 */ + adv7511_wr_and_or(sd, 0x17, 0xf9, 0x06); + /* Disable pixel repetition */ + adv7511_wr_and_or(sd, 0x3b, 0x9f, 0x0); + /* Disable CSC */ + adv7511_wr_and_or(sd, 0x18, 0x7f, 0x0); + /* Output format: RGB 4:4:4, Active Format Information is valid, + * underscanned */ + adv7511_wr_and_or(sd, 0x55, 0x9c, 0x12); + /* AVI Info frame packet enable, Audio Info frame disable */ + adv7511_wr_and_or(sd, 0x44, 0xe7, 0x10); + /* Colorimetry, Active format aspect ratio: same as picure. */ + adv7511_wr(sd, 0x56, 0xa8); + /* No encryption */ + adv7511_wr_and_or(sd, 0xaf, 0xed, 0x0); + + /* Positive clk edge capture for input video clock */ + adv7511_wr_and_or(sd, 0xba, 0x1f, 0x60); + + adv7511_audio_setup(sd); + + v4l2_ctrl_handler_setup(&state->hdl); +} + +static void adv7511_notify_monitor_detect(struct v4l2_subdev *sd) +{ + struct adv7511_monitor_detect mdt; + struct adv7511_state *state = get_adv7511_state(sd); + + mdt.present = state->have_monitor; + v4l2_subdev_notify(sd, ADV7511_MONITOR_DETECT, (void *)&mdt); +} + +static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + /* read hotplug and rx-sense state */ + uint8_t status = adv7511_rd(sd, 0x42); + + v4l2_dbg(1, debug, sd, "%s: status: 0x%x%s%s\n", + __func__, + status, + status & MASK_ADV7511_HPD_DETECT ? ", hotplug" : "", + status & MASK_ADV7511_MSEN_DETECT ? ", rx-sense" : ""); + + /* update read only ctrls */ + v4l2_ctrl_s_ctrl(state->hotplug_ctrl, adv7511_have_hotplug(sd) ? 0x1 : 0x0); + v4l2_ctrl_s_ctrl(state->rx_sense_ctrl, adv7511_have_rx_sense(sd) ? 0x1 : 0x0); + v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); + + if ((status & MASK_ADV7511_HPD_DETECT) && ((status & MASK_ADV7511_MSEN_DETECT) || state->edid.segments)) { + v4l2_dbg(1, debug, sd, "%s: hotplug and (rx-sense or edid)\n", __func__); + if (!state->have_monitor) { + v4l2_dbg(1, debug, sd, "%s: monitor detected\n", __func__); + state->have_monitor = true; + adv7511_set_isr(sd, true); + if (!adv7511_s_power(sd, true)) { + v4l2_dbg(1, debug, sd, "%s: monitor detected, powerup failed\n", __func__); + return; + } + adv7511_setup(sd); + adv7511_notify_monitor_detect(sd); + state->edid.read_retries = EDID_MAX_RETRIES; + queue_delayed_work(state->work_queue, &state->edid_handler, EDID_DELAY); + } + } else if (status & MASK_ADV7511_HPD_DETECT) { + v4l2_dbg(1, debug, sd, "%s: hotplug detected\n", __func__); + state->edid.read_retries = EDID_MAX_RETRIES; + queue_delayed_work(state->work_queue, &state->edid_handler, EDID_DELAY); + } else if (!(status & MASK_ADV7511_HPD_DETECT)) { + v4l2_dbg(1, debug, sd, "%s: hotplug not detected\n", __func__); + if (state->have_monitor) { + v4l2_dbg(1, debug, sd, "%s: monitor not detected\n", __func__); + state->have_monitor = false; + adv7511_notify_monitor_detect(sd); + } + adv7511_s_power(sd, false); + memset(&state->edid, 0, sizeof(struct adv7511_state_edid)); + } +} + +static bool edid_block_verify_crc(uint8_t *edid_block) +{ + int i; + uint8_t sum = 0; + + for (i = 0; i < 128; i++) + sum += *(edid_block + i); + return (sum == 0); +} + +static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment) +{ + struct adv7511_state *state = get_adv7511_state(sd); + u32 blocks = state->edid.blocks; + uint8_t *data = state->edid.data; + + if (edid_block_verify_crc(&data[segment * 256])) { + if ((segment + 1) * 2 <= blocks) + return edid_block_verify_crc(&data[segment * 256 + 128]); + return true; + } + return false; +} + +static bool adv7511_check_edid_status(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + uint8_t edidRdy = adv7511_rd(sd, 0xc5); + + v4l2_dbg(1, debug, sd, "%s: edid ready (retries: %d)\n", + __func__, EDID_MAX_RETRIES - state->edid.read_retries); + + if (state->edid.complete) + return true; + + if (edidRdy & MASK_ADV7511_EDID_RDY) { + int segment = adv7511_rd(sd, 0xc4); + struct adv7511_edid_detect ed; + + if (segment >= EDID_MAX_SEGM) { + v4l2_err(sd, "edid segment number too big\n"); + return false; + } + v4l2_dbg(1, debug, sd, "%s: got segment %d\n", __func__, segment); + adv7511_edid_rd(sd, 256, &state->edid.data[segment * 256]); + adv7511_dbg_dump_edid(2, debug, sd, segment, &state->edid.data[segment * 256]); + if (segment == 0) { + state->edid.blocks = state->edid.data[0x7e] + 1; + v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n", __func__, state->edid.blocks); + } + if (!edid_segment_verify_crc(sd, segment)) { + /* edid crc error, force reread of edid segment */ + v4l2_dbg(1, debug, sd, "%s: edid crc error\n", __func__); + state->have_monitor = false; + adv7511_s_power(sd, false); + adv7511_s_power(sd, true); + return false; + } + /* one more segment read ok */ + state->edid.segments = segment + 1; + if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) { + /* Request next EDID segment */ + v4l2_dbg(1, debug, sd, "%s: request segment %d\n", __func__, state->edid.segments); + adv7511_wr(sd, 0xc9, 0xf); + adv7511_wr(sd, 0xc4, state->edid.segments); + state->edid.read_retries = EDID_MAX_RETRIES; + queue_delayed_work(state->work_queue, &state->edid_handler, EDID_DELAY); + return false; + } + + v4l2_dbg(1, debug, sd, "%s: edid complete with %d segment(s)\n", __func__, state->edid.segments); + state->edid.complete = true; + + /* report when we have all segments + but report only for segment 0 + */ + ed.present = true; + ed.segment = 0; + state->edid_detect_counter++; + v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); + v4l2_subdev_notify(sd, ADV7511_EDID_DETECT, (void *)&ed); + return ed.present; + } + + return false; +} + +/* ----------------------------------------------------------------------- */ +/* Setup ADV7511 */ +static void adv7511_init_setup(struct v4l2_subdev *sd) +{ + struct adv7511_state *state = get_adv7511_state(sd); + struct adv7511_state_edid *edid = &state->edid; + + v4l2_dbg(1, debug, sd, "%s\n", __func__); + + /* clear all interrupts */ + adv7511_wr(sd, 0x96, 0xff); + memset(edid, 0, sizeof(struct adv7511_state_edid)); + state->have_monitor = false; + adv7511_set_isr(sd, false); + adv7511_s_stream(sd, false); + adv7511_s_audio_stream(sd, false); +} + +static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct adv7511_state *state; + struct adv7511_platform_data *pdata = client->dev.platform_data; + struct v4l2_ctrl_handler *hdl; + struct v4l2_subdev *sd; + u8 chip_id[2]; + int err = -EIO; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + state = devm_kzalloc(&client->dev, sizeof(struct adv7511_state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + /* Platform data */ + if (!pdata) { + v4l_err(client, "No platform data!\n"); + return -ENODEV; + } + memcpy(&state->pdata, pdata, sizeof(state->pdata)); + + sd = &state->sd; + + v4l2_dbg(1, debug, sd, "detecting adv7511 client on address 0x%x\n", + client->addr << 1); + + v4l2_i2c_subdev_init(sd, client, &adv7511_ops); + + hdl = &state->hdl; + v4l2_ctrl_handler_init(hdl, 10); + /* add in ascending ID order */ + state->hdmi_mode_ctrl = v4l2_ctrl_new_std_menu(hdl, &adv7511_ctrl_ops, + V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI, + 0, V4L2_DV_TX_MODE_DVI_D); + state->hotplug_ctrl = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_DV_TX_HOTPLUG, 0, 1, 0, 0); + state->rx_sense_ctrl = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_DV_TX_RXSENSE, 0, 1, 0, 0); + state->have_edid0_ctrl = v4l2_ctrl_new_std(hdl, NULL, + V4L2_CID_DV_TX_EDID_PRESENT, 0, 1, 0, 0); + state->rgb_quantization_range_ctrl = + v4l2_ctrl_new_std_menu(hdl, &adv7511_ctrl_ops, + V4L2_CID_DV_TX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, + 0, V4L2_DV_RGB_RANGE_AUTO); + sd->ctrl_handler = hdl; + if (hdl->error) { + err = hdl->error; + goto err_hdl; + } + state->hdmi_mode_ctrl->is_private = true; + state->hotplug_ctrl->is_private = true; + state->rx_sense_ctrl->is_private = true; + state->have_edid0_ctrl->is_private = true; + state->rgb_quantization_range_ctrl->is_private = true; + + state->pad.flags = MEDIA_PAD_FL_SINK; + err = media_entity_init(&sd->entity, 1, &state->pad, 0); + if (err) + goto err_hdl; + + /* EDID and CEC i2c addr */ + state->i2c_edid_addr = state->pdata.i2c_edid << 1; + state->i2c_cec_addr = state->pdata.i2c_cec << 1; + + state->chip_revision = adv7511_rd(sd, 0x0); + chip_id[0] = adv7511_rd(sd, 0xf5); + chip_id[1] = adv7511_rd(sd, 0xf6); + if (chip_id[0] != 0x75 || chip_id[1] != 0x11) { + v4l2_err(sd, "chip_id != 0x7511, read 0x%02x%02x\n", chip_id[0], chip_id[1]); + err = -EIO; + goto err_entity; + } + + state->i2c_edid = i2c_new_dummy(client->adapter, state->i2c_edid_addr >> 1); + if (state->i2c_edid == NULL) { + v4l2_err(sd, "failed to register edid i2c client\n"); + goto err_entity; + } + + adv7511_wr(sd, 0xe2, 0x01); /* power down cec section */ + state->work_queue = create_singlethread_workqueue(sd->name); + if (state->work_queue == NULL) { + v4l2_err(sd, "could not create workqueue\n"); + goto err_unreg_cec; + } + + INIT_DELAYED_WORK(&state->edid_handler, adv7511_edid_handler); + + adv7511_init_setup(sd); + adv7511_set_isr(sd, true); + adv7511_check_monitor_present_status(sd); + + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, + client->addr << 1, client->adapter->name); + return 0; + +err_unreg_cec: + i2c_unregister_device(state->i2c_edid); +err_entity: + media_entity_cleanup(&sd->entity); +err_hdl: + v4l2_ctrl_handler_free(&state->hdl); + return err; +} + +/* ----------------------------------------------------------------------- */ + +static int adv7511_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7511_state *state = get_adv7511_state(sd); + + state->chip_revision = -1; + + v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name, + client->addr << 1, client->adapter->name); + + adv7511_init_setup(sd); + cancel_delayed_work(&state->edid_handler); + i2c_unregister_device(state->i2c_edid); + destroy_workqueue(state->work_queue); + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_device_id adv7511_id[] = { + { "adv7511", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adv7511_id); + +static struct i2c_driver adv7511_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7511", + }, + .probe = adv7511_probe, + .remove = adv7511_remove, + .id_table = adv7511_id, +}; + +module_i2c_driver(adv7511_driver); diff --git a/include/media/adv7511.h b/include/media/adv7511.h new file mode 100644 index 000000000000..bb78bed9a5b8 --- /dev/null +++ b/include/media/adv7511.h @@ -0,0 +1,48 @@ +/* + * Analog Devices ADV7511 HDMI Transmitter Device Driver + * + * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ADV7511_H +#define ADV7511_H + +/* notify events */ +#define ADV7511_MONITOR_DETECT 0 +#define ADV7511_EDID_DETECT 1 + + +struct adv7511_monitor_detect { + int present; +}; + +struct adv7511_edid_detect { + int present; + int segment; +}; + +struct adv7511_cec_arg { + void *arg; + u32 f_flags; +}; + +struct adv7511_platform_data { + uint8_t i2c_edid; + uint8_t i2c_cec; + uint32_t cec_clk; +}; + +#endif -- cgit v1.2.3-71-gd317 From 444c580f7e9ad29927a5d5269d576bd7cdccebb8 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 8 Aug 2013 15:16:22 +0900 Subject: f2fs: add flags for inline xattrs This patch adds basic inode flags for inline xattrs, F2FS_INLINE_XATTR, and add a mount option, inline_xattr, which is enabled when xattr is set. If the mount option is enabled, all the files are marked with the inline_xattrs flag. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 18 ++++++++++++++++++ fs/f2fs/inode.c | 2 ++ fs/f2fs/super.c | 14 ++++++++++++++ include/linux/f2fs_fs.h | 4 +++- 4 files changed, 37 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5348b63adbe9..b82f14199921 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -29,6 +29,7 @@ #define F2FS_MOUNT_XATTR_USER 0x00000010 #define F2FS_MOUNT_POSIX_ACL 0x00000020 #define F2FS_MOUNT_DISABLE_EXT_IDENTIFY 0x00000040 +#define F2FS_MOUNT_INLINE_XATTR 0x00000080 #define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option) @@ -892,6 +893,7 @@ enum { FI_NO_ALLOC, /* should not allocate any blocks */ FI_UPDATE_DIR, /* should update inode block for consistency */ FI_DELAY_IPUT, /* used for the recovery */ + FI_INLINE_XATTR, /* used for inline xattr */ }; static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag) @@ -924,6 +926,22 @@ static inline int cond_clear_inode_flag(struct f2fs_inode_info *fi, int flag) return 0; } +static inline void get_inline_info(struct f2fs_inode_info *fi, + struct f2fs_inode *ri) +{ + if (ri->i_inline & F2FS_INLINE_XATTR) + set_inode_flag(fi, FI_INLINE_XATTR); +} + +static inline void set_raw_inline(struct f2fs_inode_info *fi, + struct f2fs_inode *ri) +{ + ri->i_inline = 0; + + if (is_inode_flag_set(fi, FI_INLINE_XATTR)) + ri->i_inline |= F2FS_INLINE_XATTR; +} + static inline int f2fs_readonly(struct super_block *sb) { return sb->s_flags & MS_RDONLY; diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 7f8569bd8759..9339cd292047 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -85,6 +85,7 @@ static int do_read_inode(struct inode *inode) fi->i_advise = ri->i_advise; fi->i_pino = le32_to_cpu(ri->i_pino); get_extent_info(&fi->ext, ri->i_ext); + get_inline_info(fi, ri); f2fs_put_page(node_page, 1); return 0; } @@ -164,6 +165,7 @@ void update_inode(struct inode *inode, struct page *node_page) ri->i_size = cpu_to_le64(i_size_read(inode)); ri->i_blocks = cpu_to_le64(inode->i_blocks); set_raw_extent(&F2FS_I(inode)->ext, &ri->i_ext); + set_raw_inline(F2FS_I(inode), ri); ri->i_atime = cpu_to_le64(inode->i_atime.tv_sec); ri->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index d28c4528eff8..70ecf484e7e5 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -47,6 +47,7 @@ enum { Opt_noacl, Opt_active_logs, Opt_disable_ext_identify, + Opt_inline_xattr, Opt_err, }; @@ -59,6 +60,7 @@ static match_table_t f2fs_tokens = { {Opt_noacl, "noacl"}, {Opt_active_logs, "active_logs=%u"}, {Opt_disable_ext_identify, "disable_ext_identify"}, + {Opt_inline_xattr, "inline_xattr"}, {Opt_err, NULL}, }; @@ -238,11 +240,18 @@ static int parse_options(struct super_block *sb, char *options) case Opt_nouser_xattr: clear_opt(sbi, XATTR_USER); break; + case Opt_inline_xattr: + set_opt(sbi, INLINE_XATTR); + break; #else case Opt_nouser_xattr: f2fs_msg(sb, KERN_INFO, "nouser_xattr options not supported"); break; + case Opt_inline_xattr: + f2fs_msg(sb, KERN_INFO, + "inline_xattr options not supported"); + break; #endif #ifdef CONFIG_F2FS_FS_POSIX_ACL case Opt_noacl: @@ -292,6 +301,9 @@ static struct inode *f2fs_alloc_inode(struct super_block *sb) set_inode_flag(fi, FI_NEW_INODE); + if (test_opt(F2FS_SB(sb), INLINE_XATTR)) + set_inode_flag(fi, FI_INLINE_XATTR); + return &fi->vfs_inode; } @@ -444,6 +456,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",user_xattr"); else seq_puts(seq, ",nouser_xattr"); + if (test_opt(sbi, INLINE_XATTR)) + seq_puts(seq, ",inline_xattr"); #endif #ifdef CONFIG_F2FS_FS_POSIX_ACL if (test_opt(sbi, POSIX_ACL)) diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 383d5e39b280..10ab11f8f99d 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -144,10 +144,12 @@ struct f2fs_extent { #define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ #define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */ +#define F2FS_INLINE_XATTR 0x01 /* file inline xattr flag */ + struct f2fs_inode { __le16 i_mode; /* file mode */ __u8 i_advise; /* file hints */ - __u8 i_reserved; /* reserved */ + __u8 i_inline; /* file inline flags */ __le32 i_uid; /* user ID */ __le32 i_gid; /* group ID */ __le32 i_links; /* links count */ -- cgit v1.2.3-71-gd317 From de93653fe31fc9439971296842dcd0280f8ab5f4 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Mon, 12 Aug 2013 21:08:03 +0900 Subject: f2fs: reserve the xattr space dynamically This patch enables the number of direct pointers inside on-disk inode block to be changed dynamically according to the size of inline xattr space. The number of direct pointers, ADDRS_PER_INODE, can be changed only if the file has inline xattr flag. The number of direct pointers that will be used by inline xattrs is defined as F2FS_INLINE_XATTR_ADDRS. Current patch assigns F2FS_INLINE_XATTR_ADDRS to 0 temporarily. Signed-off-by: Jaegeuk Kim --- fs/f2fs/data.c | 5 +++-- fs/f2fs/f2fs.h | 18 ++++++++---------- fs/f2fs/file.c | 2 +- fs/f2fs/gc.c | 9 ++++++--- fs/f2fs/node.c | 9 +++++---- fs/f2fs/recovery.c | 13 +++++++++---- fs/f2fs/super.c | 2 +- include/linux/f2fs_fs.h | 16 ++++++++++++---- 8 files changed, 45 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 6b328de41728..941f9b9ca3a5 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -117,7 +117,8 @@ void update_extent_cache(block_t blk_addr, struct dnode_of_data *dn) block_t start_blkaddr, end_blkaddr; BUG_ON(blk_addr == NEW_ADDR); - fofs = start_bidx_of_node(ofs_of_node(dn->node_page)) + dn->ofs_in_node; + fofs = start_bidx_of_node(ofs_of_node(dn->node_page), fi) + + dn->ofs_in_node; /* Update the page address in the parent node */ __set_data_blkaddr(dn, blk_addr); @@ -448,7 +449,7 @@ static int get_data_block_ro(struct inode *inode, sector_t iblock, unsigned int end_offset; end_offset = IS_INODE(dn.node_page) ? - ADDRS_PER_INODE : + ADDRS_PER_INODE(F2FS_I(inode)) : ADDRS_PER_BLOCK; clear_buffer_new(bh_result); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index b82f14199921..0343759ceb4c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -300,15 +300,6 @@ struct f2fs_sm_info { unsigned int ovp_segments; /* # of overprovision segments */ }; -/* - * For directory operation - */ -#define NODE_DIR1_BLOCK (ADDRS_PER_INODE + 1) -#define NODE_DIR2_BLOCK (ADDRS_PER_INODE + 2) -#define NODE_IND1_BLOCK (ADDRS_PER_INODE + 3) -#define NODE_IND2_BLOCK (ADDRS_PER_INODE + 4) -#define NODE_DIND_BLOCK (ADDRS_PER_INODE + 5) - /* * For superblock */ @@ -942,6 +933,13 @@ static inline void set_raw_inline(struct f2fs_inode_info *fi, ri->i_inline |= F2FS_INLINE_XATTR; } +static inline unsigned int addrs_per_inode(struct f2fs_inode_info *fi) +{ + if (is_inode_flag_set(fi, FI_INLINE_XATTR)) + return DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS; + return DEF_ADDRS_PER_INODE; +} + static inline int f2fs_readonly(struct super_block *sb) { return sb->s_flags & MS_RDONLY; @@ -1108,7 +1106,7 @@ int do_write_data_page(struct page *); */ int start_gc_thread(struct f2fs_sb_info *); void stop_gc_thread(struct f2fs_sb_info *); -block_t start_bidx_of_node(unsigned int); +block_t start_bidx_of_node(unsigned int, struct f2fs_inode_info *); int f2fs_gc(struct f2fs_sb_info *); void build_gc_manager(struct f2fs_sb_info *); int __init create_gc_caches(void); diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index bd4184e35525..02c906971cc6 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -290,7 +290,7 @@ static int truncate_blocks(struct inode *inode, u64 from) } if (IS_INODE(dn.node_page)) - count = ADDRS_PER_INODE; + count = ADDRS_PER_INODE(F2FS_I(inode)); else count = ADDRS_PER_BLOCK; diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index e6b3ffd5ff6a..eb89037e3312 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -461,7 +461,7 @@ next_step: * as indirect or double indirect node blocks, are given, it must be a caller's * bug. */ -block_t start_bidx_of_node(unsigned int node_ofs) +block_t start_bidx_of_node(unsigned int node_ofs, struct f2fs_inode_info *fi) { unsigned int indirect_blks = 2 * NIDS_PER_BLOCK + 4; unsigned int bidx; @@ -478,7 +478,7 @@ block_t start_bidx_of_node(unsigned int node_ofs) int dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1); bidx = node_ofs - 5 - dec; } - return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE; + return bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi); } static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, @@ -586,7 +586,6 @@ next_step: continue; } - start_bidx = start_bidx_of_node(nofs); ofs_in_node = le16_to_cpu(entry->ofs_in_node); if (phase == 2) { @@ -594,6 +593,8 @@ next_step: if (IS_ERR(inode)) continue; + start_bidx = start_bidx_of_node(nofs, F2FS_I(inode)); + data_page = find_data_page(inode, start_bidx + ofs_in_node, false); if (IS_ERR(data_page)) @@ -604,6 +605,8 @@ next_step: } else { inode = find_gc_inode(dni.ino, ilist); if (inode) { + start_bidx = start_bidx_of_node(nofs, + F2FS_I(inode)); data_page = get_lock_data_page(inode, start_bidx + ofs_in_node); if (IS_ERR(data_page)) diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 818ff368de81..f0e733b21b2a 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -315,9 +315,10 @@ cache: * The maximum depth is four. * Offset[0] will have raw inode offset. */ -static int get_node_path(long block, int offset[4], unsigned int noffset[4]) +static int get_node_path(struct f2fs_inode_info *fi, long block, + int offset[4], unsigned int noffset[4]) { - const long direct_index = ADDRS_PER_INODE; + const long direct_index = ADDRS_PER_INODE(fi); const long direct_blks = ADDRS_PER_BLOCK; const long dptrs_per_blk = NIDS_PER_BLOCK; const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK; @@ -405,7 +406,7 @@ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode) int level, i; int err = 0; - level = get_node_path(index, offset, noffset); + level = get_node_path(F2FS_I(dn->inode), index, offset, noffset); nids[0] = dn->inode->i_ino; npage[0] = dn->inode_page; @@ -687,7 +688,7 @@ int truncate_inode_blocks(struct inode *inode, pgoff_t from) trace_f2fs_truncate_inode_blocks_enter(inode, from); - level = get_node_path(from, offset, noffset); + level = get_node_path(F2FS_I(inode), from, offset, noffset); restart: page = get_node_page(sbi, inode->i_ino); if (IS_ERR(page)) { diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index fa493bb64167..51ef5eec33d7 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -213,6 +213,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, void *kaddr; struct inode *inode; struct page *node_page; + unsigned int offset; block_t bidx; int i; @@ -257,8 +258,8 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, node_page = get_node_page(sbi, nid); if (IS_ERR(node_page)) return PTR_ERR(node_page); - bidx = start_bidx_of_node(ofs_of_node(node_page)) + - le16_to_cpu(sum.ofs_in_node); + + offset = ofs_of_node(node_page); ino = ino_of_node(node_page); f2fs_put_page(node_page, 1); @@ -267,6 +268,9 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, if (IS_ERR(inode)) return PTR_ERR(inode); + bidx = start_bidx_of_node(offset, F2FS_I(inode)) + + le16_to_cpu(sum.ofs_in_node); + truncate_hole(inode, bidx, bidx + 1); iput(inode); return 0; @@ -275,6 +279,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, struct page *page, block_t blkaddr) { + struct f2fs_inode_info *fi = F2FS_I(inode); unsigned int start, end; struct dnode_of_data dn; struct f2fs_summary sum; @@ -282,9 +287,9 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, int err = 0, recovered = 0; int ilock; - start = start_bidx_of_node(ofs_of_node(page)); + start = start_bidx_of_node(ofs_of_node(page), fi); if (IS_INODE(page)) - end = start + ADDRS_PER_INODE; + end = start + ADDRS_PER_INODE(fi); else end = start + ADDRS_PER_BLOCK; diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 70ecf484e7e5..13d0a0fe49dd 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -618,7 +618,7 @@ static const struct export_operations f2fs_export_ops = { static loff_t max_file_size(unsigned bits) { - loff_t result = ADDRS_PER_INODE; + loff_t result = (DEF_ADDRS_PER_INODE - F2FS_INLINE_XATTR_ADDRS); loff_t leaf_count = ADDRS_PER_BLOCK; /* two direct node blocks */ diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 10ab11f8f99d..93e7020fb7a8 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -140,9 +140,17 @@ struct f2fs_extent { } __packed; #define F2FS_NAME_LEN 255 -#define ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ -#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ -#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */ +#define F2FS_INLINE_XATTR_ADDRS 0 /* 0 bytes for inline xattrs */ +#define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ +#define ADDRS_PER_INODE(fi) addrs_per_inode(fi) +#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ +#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */ + +#define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1) +#define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2) +#define NODE_IND1_BLOCK (DEF_ADDRS_PER_INODE + 3) +#define NODE_IND2_BLOCK (DEF_ADDRS_PER_INODE + 4) +#define NODE_DIND_BLOCK (DEF_ADDRS_PER_INODE + 5) #define F2FS_INLINE_XATTR 0x01 /* file inline xattr flag */ @@ -172,7 +180,7 @@ struct f2fs_inode { struct f2fs_extent i_ext; /* caching a largest extent */ - __le32 i_addr[ADDRS_PER_INODE]; /* Pointers to data blocks */ + __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ __le32 i_nid[5]; /* direct(2), indirect(2), double_indirect(1) node id */ -- cgit v1.2.3-71-gd317 From 65985d935ddd5657c66a8bb3ae9752ed842549b8 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 14 Aug 2013 21:57:27 +0900 Subject: f2fs: support the inline xattrs 0. modified inode structure -------------------------------------- metadata (e.g., i_mtime, i_ctime, etc) -------------------------------------- direct pointers [0 ~ 873] inline xattrs (200 bytes by default) indirect pointers [0 ~ 4] -------------------------------------- node footer -------------------------------------- 1. setxattr flow - read_all_xattrs copies all the xattrs from inline and xattr node block. - handle xattr entries - write_all_xattrs copies modified xattrs into inline and xattr node block. 2. getxattr flow - read_all_xattrs copies all the xattrs from inline and xattr node block. - check target entries 3. Usage # mount -t f2fs -o inline_xattr $DEV $MNT Once mounted with the inline_xattr option, f2fs marks all the newly created files to reserve an amount of inline xattr space explicitly inside the inode block. Without the mount option, f2fs will not touch any existing files and newly created files as well. Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 16 ++++ fs/f2fs/node.c | 7 ++ fs/f2fs/xattr.c | 249 ++++++++++++++++++++++++++++++++---------------- fs/f2fs/xattr.h | 12 +-- include/linux/f2fs_fs.h | 2 +- 5 files changed, 196 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 0fe9302e3e17..608f0df5b919 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -940,6 +940,22 @@ static inline unsigned int addrs_per_inode(struct f2fs_inode_info *fi) return DEF_ADDRS_PER_INODE; } +static inline void *inline_xattr_addr(struct page *page) +{ + struct f2fs_inode *ri; + ri = (struct f2fs_inode *)page_address(page); + return (void *)&(ri->i_addr[DEF_ADDRS_PER_INODE - + F2FS_INLINE_XATTR_ADDRS]); +} + +static inline int inline_xattr_size(struct inode *inode) +{ + if (is_inode_flag_set(F2FS_I(inode), FI_INLINE_XATTR)) + return F2FS_INLINE_XATTR_ADDRS << 2; + else + return 0; +} + static inline int f2fs_readonly(struct super_block *sb) { return sb->s_flags & MS_RDONLY; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 2a464a76602d..c3c03c975bd6 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -787,6 +787,10 @@ int truncate_xattr_node(struct inode *inode, struct page *page) return PTR_ERR(npage); F2FS_I(inode)->i_xattr_nid = 0; + + /* need to do checkpoint during fsync */ + F2FS_I(inode)->xattr_ver = cur_cp_version(F2FS_CKPT(sbi)); + set_new_dnode(&dn, inode, page, npage, nid); if (page) @@ -1464,6 +1468,9 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) struct f2fs_nm_info *nm_i = NM_I(sbi); struct free_nid *i; + if (!nid) + return; + spin_lock(&nm_i->free_nid_list_lock); i = __lookup_free_nid_list(nid, &nm_i->free_nid_list); BUG_ON(!i || i->state != NID_ALLOC); diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 45a8ef882f89..1ac8a5f6e380 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -262,13 +262,141 @@ static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int name_index, return entry; } +static void *read_all_xattrs(struct inode *inode, struct page *ipage) +{ + struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); + struct f2fs_xattr_header *header; + size_t size = PAGE_SIZE, inline_size = 0; + void *txattr_addr; + + inline_size = inline_xattr_size(inode); + + txattr_addr = kzalloc(inline_size + size, GFP_KERNEL); + if (!txattr_addr) + return NULL; + + /* read from inline xattr */ + if (inline_size) { + struct page *page = NULL; + void *inline_addr; + + if (ipage) { + inline_addr = inline_xattr_addr(ipage); + } else { + page = get_node_page(sbi, inode->i_ino); + if (IS_ERR(page)) + goto fail; + inline_addr = inline_xattr_addr(page); + } + memcpy(txattr_addr, inline_addr, inline_size); + f2fs_put_page(page, 1); + } + + /* read from xattr node block */ + if (F2FS_I(inode)->i_xattr_nid) { + struct page *xpage; + void *xattr_addr; + + /* The inode already has an extended attribute block. */ + xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid); + if (IS_ERR(xpage)) + goto fail; + + xattr_addr = page_address(xpage); + memcpy(txattr_addr + inline_size, xattr_addr, PAGE_SIZE); + f2fs_put_page(xpage, 1); + } + + header = XATTR_HDR(txattr_addr); + + /* never been allocated xattrs */ + if (le32_to_cpu(header->h_magic) != F2FS_XATTR_MAGIC) { + header->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC); + header->h_refcount = cpu_to_le32(1); + } + return txattr_addr; +fail: + kzfree(txattr_addr); + return NULL; +} + +static inline int write_all_xattrs(struct inode *inode, __u32 hsize, + void *txattr_addr, struct page *ipage) +{ + struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); + size_t inline_size = 0; + void *xattr_addr; + struct page *xpage; + nid_t new_nid = 0; + int err; + + inline_size = inline_xattr_size(inode); + + if (hsize > inline_size && !F2FS_I(inode)->i_xattr_nid) + if (!alloc_nid(sbi, &new_nid)) + return -ENOSPC; + + /* write to inline xattr */ + if (inline_size) { + struct page *page = NULL; + void *inline_addr; + + if (ipage) { + inline_addr = inline_xattr_addr(ipage); + } else { + page = get_node_page(sbi, inode->i_ino); + if (IS_ERR(page)) { + alloc_nid_failed(sbi, new_nid); + return PTR_ERR(page); + } + inline_addr = inline_xattr_addr(page); + } + memcpy(inline_addr, txattr_addr, inline_size); + f2fs_put_page(page, 1); + + /* no need to use xattr node block */ + if (hsize <= inline_size) { + err = truncate_xattr_node(inode, ipage); + alloc_nid_failed(sbi, new_nid); + return err; + } + } + + /* write to xattr node block */ + if (F2FS_I(inode)->i_xattr_nid) { + xpage = get_node_page(sbi, F2FS_I(inode)->i_xattr_nid); + if (IS_ERR(xpage)) { + alloc_nid_failed(sbi, new_nid); + return PTR_ERR(xpage); + } + BUG_ON(new_nid); + } else { + struct dnode_of_data dn; + set_new_dnode(&dn, inode, NULL, NULL, new_nid); + xpage = new_node_page(&dn, XATTR_NODE_OFFSET, ipage); + if (IS_ERR(xpage)) { + alloc_nid_failed(sbi, new_nid); + return PTR_ERR(xpage); + } + alloc_nid_done(sbi, new_nid); + } + + xattr_addr = page_address(xpage); + memcpy(xattr_addr, txattr_addr + inline_size, PAGE_SIZE - + sizeof(struct node_footer)); + set_page_dirty(xpage); + f2fs_put_page(xpage, 1); + + /* need to checkpoint during fsync */ + F2FS_I(inode)->xattr_ver = cur_cp_version(F2FS_CKPT(sbi)); + return 0; +} + int f2fs_getxattr(struct inode *inode, int name_index, const char *name, void *buffer, size_t buffer_size) { - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_xattr_entry *entry; - struct page *page; + void *base_addr; int error = 0; size_t value_len, name_len; @@ -276,14 +404,11 @@ int f2fs_getxattr(struct inode *inode, int name_index, const char *name, return -EINVAL; name_len = strlen(name); - if (!fi->i_xattr_nid) - return -ENODATA; - - page = get_node_page(sbi, fi->i_xattr_nid); - if (IS_ERR(page)) - return PTR_ERR(page); + base_addr = read_all_xattrs(inode, NULL); + if (!base_addr) + return -ENOMEM; - entry = __find_xattr(page_address(page), name_index, name_len, name); + entry = __find_xattr(base_addr, name_index, name_len, name); if (IS_XATTR_LAST_ENTRY(entry)) { error = -ENODATA; goto cleanup; @@ -303,28 +428,21 @@ int f2fs_getxattr(struct inode *inode, int name_index, const char *name, error = value_len; cleanup: - f2fs_put_page(page, 1); + kzfree(base_addr); return error; } ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) { struct inode *inode = dentry->d_inode; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_xattr_entry *entry; - struct page *page; void *base_addr; int error = 0; size_t rest = buffer_size; - if (!fi->i_xattr_nid) - return 0; - - page = get_node_page(sbi, fi->i_xattr_nid); - if (IS_ERR(page)) - return PTR_ERR(page); - base_addr = page_address(page); + base_addr = read_all_xattrs(inode, NULL); + if (!base_addr) + return -ENOMEM; list_for_each_xattr(entry, base_addr) { const struct xattr_handler *handler = @@ -347,7 +465,7 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) } error = buffer_size - rest; cleanup: - f2fs_put_page(page, 1); + kzfree(base_addr); return error; } @@ -356,14 +474,13 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name, { struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); struct f2fs_inode_info *fi = F2FS_I(inode); - struct f2fs_xattr_header *header = NULL; struct f2fs_xattr_entry *here, *last; - struct page *page; void *base_addr; - int error, found, free, newsize; + int found, newsize; size_t name_len; - char *pval; int ilock; + __u32 new_hsize; + int error = -ENOMEM; if (name == NULL) return -EINVAL; @@ -373,53 +490,16 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name, name_len = strlen(name); - if (name_len > F2FS_NAME_LEN || value_len > MAX_VALUE_LEN) + if (name_len > F2FS_NAME_LEN || value_len > MAX_VALUE_LEN(inode)) return -ERANGE; f2fs_balance_fs(sbi); ilock = mutex_lock_op(sbi); - if (!fi->i_xattr_nid) { - /* Allocate new attribute block */ - struct dnode_of_data dn; - nid_t new_nid; - - if (!alloc_nid(sbi, &new_nid)) { - error = -ENOSPC; - goto exit; - } - set_new_dnode(&dn, inode, NULL, NULL, new_nid); - mark_inode_dirty(inode); - - page = new_node_page(&dn, XATTR_NODE_OFFSET, ipage); - if (IS_ERR(page)) { - alloc_nid_failed(sbi, new_nid); - error = PTR_ERR(page); - goto exit; - } - - alloc_nid_done(sbi, new_nid); - base_addr = page_address(page); - header = XATTR_HDR(base_addr); - header->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC); - header->h_refcount = cpu_to_le32(1); - } else { - /* The inode already has an extended attribute block. */ - page = get_node_page(sbi, fi->i_xattr_nid); - if (IS_ERR(page)) { - error = PTR_ERR(page); - goto exit; - } - - base_addr = page_address(page); - header = XATTR_HDR(base_addr); - } - - if (le32_to_cpu(header->h_magic) != F2FS_XATTR_MAGIC) { - error = -EIO; - goto cleanup; - } + base_addr = read_all_xattrs(inode, ipage); + if (!base_addr) + goto exit; /* find entry with wanted name. */ here = __find_xattr(base_addr, name_index, name_len, name); @@ -435,22 +515,25 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name, /* 1. Check space */ if (value) { - /* If value is NULL, it is remove operation. + int free; + /* + * If value is NULL, it is remove operation. * In case of update operation, we caculate free. */ - free = MIN_OFFSET - ((char *)last - (char *)header); + free = MIN_OFFSET(inode) - ((char *)last - (char *)base_addr); if (found) free = free - ENTRY_SIZE(here); if (free < newsize) { error = -ENOSPC; - goto cleanup; + goto exit; } } /* 2. Remove old entry */ if (found) { - /* If entry is found, remove old entry. + /* + * If entry is found, remove old entry. * If not found, remove operation is not needed. */ struct f2fs_xattr_entry *next = XATTR_NEXT_ENTRY(here); @@ -461,10 +544,15 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name, memset(last, 0, oldsize); } + new_hsize = (char *)last - (char *)base_addr; + /* 3. Write new entry */ if (value) { - /* Before we come here, old entry is removed. - * We just write new entry. */ + char *pval; + /* + * Before we come here, old entry is removed. + * We just write new entry. + */ memset(last, 0, newsize); last->e_name_index = name_index; last->e_name_len = name_len; @@ -472,10 +560,12 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name, pval = last->e_name + name_len; memcpy(pval, value, value_len); last->e_value_size = cpu_to_le16(value_len); + new_hsize += newsize; } - set_page_dirty(page); - f2fs_put_page(page, 1); + error = write_all_xattrs(inode, new_hsize, base_addr, ipage); + if (error) + goto exit; if (is_inode_flag_set(fi, FI_ACL_MODE)) { inode->i_mode = fi->i_acl_mode; @@ -483,19 +573,12 @@ int f2fs_setxattr(struct inode *inode, int name_index, const char *name, clear_inode_flag(fi, FI_ACL_MODE); } - /* store checkpoint version for conducting checkpoint during fsync */ - fi->xattr_ver = cur_cp_version(F2FS_CKPT(sbi)); - if (ipage) update_inode(inode, ipage); else update_inode_page(inode); - mutex_unlock_op(sbi, ilock); - - return 0; -cleanup: - f2fs_put_page(page, 1); exit: mutex_unlock_op(sbi, ilock); + kzfree(base_addr); return error; } diff --git a/fs/f2fs/xattr.h b/fs/f2fs/xattr.h index c3ec042707d4..02a08fb88a15 100644 --- a/fs/f2fs/xattr.h +++ b/fs/f2fs/xattr.h @@ -69,16 +69,16 @@ struct f2fs_xattr_entry { !IS_XATTR_LAST_ENTRY(entry);\ entry = XATTR_NEXT_ENTRY(entry)) -#define MIN_OFFSET XATTR_ALIGN(PAGE_SIZE - \ - sizeof(struct node_footer) - \ - sizeof(__u32)) +#define MIN_OFFSET(i) XATTR_ALIGN(inline_xattr_size(i) + PAGE_SIZE - \ + sizeof(struct node_footer) - sizeof(__u32)) -#define MAX_VALUE_LEN (MIN_OFFSET - sizeof(struct f2fs_xattr_header) - \ - sizeof(struct f2fs_xattr_entry)) +#define MAX_VALUE_LEN(i) (MIN_OFFSET(i) - \ + sizeof(struct f2fs_xattr_header) - \ + sizeof(struct f2fs_xattr_entry)) /* * On-disk structure of f2fs_xattr - * We use only 1 block for xattr. + * We use inline xattrs space + 1 block for xattr. * * +--------------------+ * | f2fs_xattr_header | diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 93e7020fb7a8..bb942f6d5702 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -140,7 +140,7 @@ struct f2fs_extent { } __packed; #define F2FS_NAME_LEN 255 -#define F2FS_INLINE_XATTR_ADDRS 0 /* 0 bytes for inline xattrs */ +#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */ #define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ #define ADDRS_PER_INODE(fi) addrs_per_inode(fi) #define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ -- cgit v1.2.3-71-gd317 From 15261f6d8d032b30f6eb7dbf1dbb9e4095df84c0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 14 Aug 2013 11:07:08 +0300 Subject: HID: hid-sensor-hub: fix style of comments This patch fixes the style of the comments to be like following /* The commentary */ There is no functional change. Signed-off-by: Andy Shevchenko Signed-off-by: Jiri Kosina --- drivers/hid/hid-sensor-hub.c | 2 +- include/linux/hid-sensor-hub.h | 2 +- include/linux/hid-sensor-ids.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index ca7498107327..ffc80cf481b4 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -416,7 +416,7 @@ static int sensor_hub_raw_event(struct hid_device *hdev, return 1; ptr = raw_data; - ptr++; /*Skip report id*/ + ptr++; /* Skip report id */ spin_lock_irqsave(&pdata->lock, flags); diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h index ecefb7311dd6..32ba45158d39 100644 --- a/include/linux/hid-sensor-hub.h +++ b/include/linux/hid-sensor-hub.h @@ -172,7 +172,7 @@ struct hid_sensor_common { struct hid_sensor_hub_attribute_info sensitivity; }; -/*Convert from hid unit expo to regular exponent*/ +/* Convert from hid unit expo to regular exponent */ static inline int hid_sensor_convert_exponent(int unit_expo) { if (unit_expo < 0x08) diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h index 6f24446e7669..4f945d3ed49f 100644 --- a/include/linux/hid-sensor-ids.h +++ b/include/linux/hid-sensor-ids.h @@ -37,7 +37,7 @@ #define HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS 0x200458 #define HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS 0x200459 -/*ORIENTATION: Compass 3D: (200083) */ +/* ORIENTATION: Compass 3D: (200083) */ #define HID_USAGE_SENSOR_COMPASS_3D 0x200083 #define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING 0x200471 #define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING_X 0x200472 -- cgit v1.2.3-71-gd317 From 0bd50dc971aad3c29043de4fb7bce45c351d1b67 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 1 Aug 2013 14:44:24 +1000 Subject: KVM: PPC: reserve a capability number for multitce support This is to reserve a capablity number for upcoming support of H_PUT_TCE_INDIRECT and H_STUFF_TCE pseries hypercalls which support mulptiple DMA map/unmap operations per one call. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Gleb Natapov --- include/uapi/linux/kvm.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index acccd08be6c7..99c25338ede8 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -667,6 +667,7 @@ struct kvm_ppc_smmu_info { #define KVM_CAP_PPC_RTAS 91 #define KVM_CAP_IRQ_XICS 92 #define KVM_CAP_ARM_EL1_32BIT 93 +#define KVM_CAP_SPAPR_MULTITCE 94 #ifdef KVM_CAP_IRQ_ROUTING -- cgit v1.2.3-71-gd317 From 279afdfe78a020b4b1a68bffd0009b961b12982e Mon Sep 17 00:00:00 2001 From: "Ewan D. Milne" Date: Thu, 8 Aug 2013 15:07:48 -0400 Subject: [SCSI] Generate uevents on certain unit attention codes Generate a uevent when the following Unit Attention ASC/ASCQ codes are received: 2A/01 MODE PARAMETERS CHANGED 2A/09 CAPACITY DATA HAS CHANGED 38/07 THIN PROVISIONING SOFT THRESHOLD REACHED 3F/03 INQUIRY DATA HAS CHANGED 3F/0E REPORTED LUNS DATA HAS CHANGED Log kernel messages when the following Unit Attention ASC/ASCQ codes are received that are not as specific as those above: 2A/xx PARAMETERS CHANGED 3F/xx TARGET OPERATING CONDITIONS HAVE CHANGED Added logic to set expecting_lun_change for other LUNs on the target after REPORTED LUNS DATA HAS CHANGED is received, so that duplicate uevents are not generated, and clear expecting_lun_change when a REPORT LUNS command completes, in accordance with the SPC-3 specification regarding reporting of the 3F 0E ASC/ASCQ UA. [jejb: remove SPC3 test in scsi_report_lun_change and some docbook fixes and unused variable fix, both reported by Fengguang Wu] Signed-off-by: Ewan D. Milne Signed-off-by: James Bottomley --- drivers/scsi/scsi_error.c | 100 ++++++++++++++++++++++++++++++++++++--------- drivers/scsi/scsi_lib.c | 26 +++++++++++- drivers/scsi/scsi_sysfs.c | 10 +++++ include/scsi/scsi_device.h | 13 +++++- 4 files changed, 127 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index a46c3dddcf70..83e591b60193 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -223,6 +223,74 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost, } #endif + /** + * scsi_report_lun_change - Set flag on all *other* devices on the same target + * to indicate that a UNIT ATTENTION is expected. + * @sdev: Device reporting the UNIT ATTENTION + */ +static void scsi_report_lun_change(struct scsi_device *sdev) +{ + sdev->sdev_target->expecting_lun_change = 1; +} + +/** + * scsi_report_sense - Examine scsi sense information and log messages for + * certain conditions, also issue uevents for some of them. + * @sdev: Device reporting the sense code + * @sshdr: sshdr to be examined + */ +static void scsi_report_sense(struct scsi_device *sdev, + struct scsi_sense_hdr *sshdr) +{ + enum scsi_device_event evt_type = SDEV_EVT_MAXBITS; /* i.e. none */ + + if (sshdr->sense_key == UNIT_ATTENTION) { + if (sshdr->asc == 0x3f && sshdr->ascq == 0x03) { + evt_type = SDEV_EVT_INQUIRY_CHANGE_REPORTED; + sdev_printk(KERN_WARNING, sdev, + "Inquiry data has changed"); + } else if (sshdr->asc == 0x3f && sshdr->ascq == 0x0e) { + evt_type = SDEV_EVT_LUN_CHANGE_REPORTED; + scsi_report_lun_change(sdev); + sdev_printk(KERN_WARNING, sdev, + "Warning! Received an indication that the " + "LUN assignments on this target have " + "changed. The Linux SCSI layer does not " + "automatically remap LUN assignments.\n"); + } else if (sshdr->asc == 0x3f) + sdev_printk(KERN_WARNING, sdev, + "Warning! Received an indication that the " + "operating parameters on this target have " + "changed. The Linux SCSI layer does not " + "automatically adjust these parameters.\n"); + + if (sshdr->asc == 0x38 && sshdr->ascq == 0x07) { + evt_type = SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED; + sdev_printk(KERN_WARNING, sdev, + "Warning! Received an indication that the " + "LUN reached a thin provisioning soft " + "threshold.\n"); + } + + if (sshdr->asc == 0x2a && sshdr->ascq == 0x01) { + evt_type = SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED; + sdev_printk(KERN_WARNING, sdev, + "Mode parameters changed"); + } else if (sshdr->asc == 0x2a && sshdr->ascq == 0x09) { + evt_type = SDEV_EVT_CAPACITY_CHANGE_REPORTED; + sdev_printk(KERN_WARNING, sdev, + "Capacity data has changed"); + } else if (sshdr->asc == 0x2a) + sdev_printk(KERN_WARNING, sdev, + "Parameters changed"); + } + + if (evt_type != SDEV_EVT_MAXBITS) { + set_bit(evt_type, sdev->pending_events); + schedule_work(&sdev->event_work); + } +} + /** * scsi_check_sense - Examine scsi cmd sense * @scmd: Cmd to have sense checked. @@ -250,6 +318,8 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) */ return SUCCESS; + scsi_report_sense(sdev, &sshdr); + if (scsi_sense_is_deferred(&sshdr)) return NEEDS_RETRY; @@ -314,6 +384,14 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) return NEEDS_RETRY; } } + /* + * we might also expect a cc/ua if another LUN on the target + * reported a UA with an ASC/ASCQ of 3F 0E - + * REPORTED LUNS DATA HAS CHANGED. + */ + if (scmd->device->sdev_target->expecting_lun_change && + sshdr.asc == 0x3f && sshdr.ascq == 0x0e) + return NEEDS_RETRY; /* * if the device is in the process of becoming ready, we * should retry. @@ -327,26 +405,6 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) if (scmd->device->allow_restart && (sshdr.asc == 0x04) && (sshdr.ascq == 0x02)) return FAILED; - - if (sshdr.asc == 0x3f && sshdr.ascq == 0x0e) - scmd_printk(KERN_WARNING, scmd, - "Warning! Received an indication that the " - "LUN assignments on this target have " - "changed. The Linux SCSI layer does not " - "automatically remap LUN assignments.\n"); - else if (sshdr.asc == 0x3f) - scmd_printk(KERN_WARNING, scmd, - "Warning! Received an indication that the " - "operating parameters on this target have " - "changed. The Linux SCSI layer does not " - "automatically adjust these parameters.\n"); - - if (sshdr.asc == 0x38 && sshdr.ascq == 0x07) - scmd_printk(KERN_WARNING, scmd, - "Warning! Received an indication that the " - "LUN reached a thin provisioning soft " - "threshold.\n"); - /* * Pass the UA upwards for a determination in the completion * functions. @@ -1574,6 +1632,8 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) */ return ADD_TO_MLQUEUE; case GOOD: + if (scmd->cmnd[0] == REPORT_LUNS) + scmd->device->sdev_target->expecting_lun_change = 0; scsi_handle_queue_ramp_up(scmd->device); case COMMAND_TERMINATED: return SUCCESS; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 36b5c898db9d..d545931c85eb 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -2253,7 +2253,21 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt) case SDEV_EVT_MEDIA_CHANGE: envp[idx++] = "SDEV_MEDIA_CHANGE=1"; break; - + case SDEV_EVT_INQUIRY_CHANGE_REPORTED: + envp[idx++] = "SDEV_UA=INQUIRY_DATA_HAS_CHANGED"; + break; + case SDEV_EVT_CAPACITY_CHANGE_REPORTED: + envp[idx++] = "SDEV_UA=CAPACITY_DATA_HAS_CHANGED"; + break; + case SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED: + envp[idx++] = "SDEV_UA=THIN_PROVISIONING_SOFT_THRESHOLD_REACHED"; + break; + case SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED: + envp[idx++] = "SDEV_UA=MODE_PARAMETERS_CHANGED"; + break; + case SDEV_EVT_LUN_CHANGE_REPORTED: + envp[idx++] = "SDEV_UA=REPORTED_LUNS_DATA_HAS_CHANGED"; + break; default: /* do nothing */ break; @@ -2274,10 +2288,15 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt) void scsi_evt_thread(struct work_struct *work) { struct scsi_device *sdev; + enum scsi_device_event evt_type; LIST_HEAD(event_list); sdev = container_of(work, struct scsi_device, event_work); + for (evt_type = SDEV_EVT_FIRST; evt_type <= SDEV_EVT_LAST; evt_type++) + if (test_and_clear_bit(evt_type, sdev->pending_events)) + sdev_evt_send_simple(sdev, evt_type, GFP_KERNEL); + while (1) { struct scsi_event *evt; struct list_head *this, *tmp; @@ -2347,6 +2366,11 @@ struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type, /* evt_type-specific initialization, if any */ switch (evt_type) { case SDEV_EVT_MEDIA_CHANGE: + case SDEV_EVT_INQUIRY_CHANGE_REPORTED: + case SDEV_EVT_CAPACITY_CHANGE_REPORTED: + case SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED: + case SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED: + case SDEV_EVT_LUN_CHANGE_REPORTED: default: /* do nothing */ break; diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 7e50061e9ef6..40c639491b27 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -739,6 +739,11 @@ sdev_store_evt_##name(struct device *dev, struct device_attribute *attr,\ #define REF_EVT(name) &dev_attr_evt_##name.attr DECLARE_EVT(media_change, MEDIA_CHANGE) +DECLARE_EVT(inquiry_change_reported, INQUIRY_CHANGE_REPORTED) +DECLARE_EVT(capacity_change_reported, CAPACITY_CHANGE_REPORTED) +DECLARE_EVT(soft_threshold_reached, SOFT_THRESHOLD_REACHED_REPORTED) +DECLARE_EVT(mode_parameter_change_reported, MODE_PARAMETER_CHANGE_REPORTED) +DECLARE_EVT(lun_change_reported, LUN_CHANGE_REPORTED) /* Default template for device attributes. May NOT be modified */ static struct attribute *scsi_sdev_attrs[] = { @@ -759,6 +764,11 @@ static struct attribute *scsi_sdev_attrs[] = { &dev_attr_ioerr_cnt.attr, &dev_attr_modalias.attr, REF_EVT(media_change), + REF_EVT(inquiry_change_reported), + REF_EVT(capacity_change_reported), + REF_EVT(soft_threshold_reached), + REF_EVT(mode_parameter_change_reported), + REF_EVT(lun_change_reported), NULL }; diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index a44954c7cdc2..d65fbec2533d 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -52,8 +52,15 @@ enum scsi_device_state { enum scsi_device_event { SDEV_EVT_MEDIA_CHANGE = 1, /* media has changed */ + SDEV_EVT_INQUIRY_CHANGE_REPORTED, /* 3F 03 UA reported */ + SDEV_EVT_CAPACITY_CHANGE_REPORTED, /* 2A 09 UA reported */ + SDEV_EVT_SOFT_THRESHOLD_REACHED_REPORTED, /* 38 07 UA reported */ + SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED, /* 2A 01 UA reported */ + SDEV_EVT_LUN_CHANGE_REPORTED, /* 3F 0E UA reported */ + + SDEV_EVT_FIRST = SDEV_EVT_MEDIA_CHANGE, + SDEV_EVT_LAST = SDEV_EVT_LUN_CHANGE_REPORTED, - SDEV_EVT_LAST = SDEV_EVT_MEDIA_CHANGE, SDEV_EVT_MAXBITS = SDEV_EVT_LAST + 1 }; @@ -164,6 +171,7 @@ struct scsi_device { atomic_t disk_events_disable_depth; /* disable depth for disk events */ DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */ + DECLARE_BITMAP(pending_events, SDEV_EVT_MAXBITS); /* pending events */ struct list_head event_list; /* asserted events */ struct work_struct event_work; @@ -261,6 +269,9 @@ struct scsi_target { * means no lun present. */ unsigned int no_report_luns:1; /* Don't use * REPORT LUNS for scanning. */ + unsigned int expecting_lun_change:1; /* A device has reported + * a 3F/0E UA, other devices on + * the same target will also. */ /* commands actually active on LLD. protected by host lock. */ unsigned int target_busy; /* -- cgit v1.2.3-71-gd317 From 50192abe02929586111fb33f216060a9341875f1 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Wed, 21 Aug 2013 18:24:47 +0100 Subject: fs/9p: avoid accessing utsname after namespace has been torn down During trinity fuzzing in a kvmtool guest, I stumbled across the following: Unable to handle kernel NULL pointer dereference at virtual address 00000004 PC is at v9fs_file_do_lock+0xc8/0x1a0 LR is at v9fs_file_do_lock+0x48/0x1a0 [] (v9fs_file_do_lock+0xc8/0x1a0) from [] (locks_remove_flock+0x8c/0x124) [] (locks_remove_flock+0x8c/0x124) from [] (__fput+0x58/0x1e4) [] (__fput+0x58/0x1e4) from [] (task_work_run+0xac/0xe8) [] (task_work_run+0xac/0xe8) from [] (do_exit+0x6bc/0x8d8) [] (do_exit+0x6bc/0x8d8) from [] (do_group_exit+0x3c/0xb0) [] (do_group_exit+0x3c/0xb0) from [] (__wake_up_parent+0x0/0x18) I believe this is due to an attempt to access utsname()->nodename, after exit_task_namespaces() has been called, leaving current->nsproxy->uts_ns as NULL and causing the above dereference. A similar issue was fixed for lockd in 9a1b6bf818e7 ("LOCKD: Don't call utsname()->nodename from nlmclnt_setlockargs"), so this patch attempts something similar for 9pfs. Cc: Eric Van Hensbergen Cc: Ron Minnich Cc: Latchesar Ionkov Cc: Trond Myklebust Signed-off-by: Will Deacon Signed-off-by: Eric Van Hensbergen --- fs/9p/vfs_file.c | 4 ++-- include/net/9p/client.h | 5 +++++ net/9p/client.c | 5 +++++ 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index d384a8b77ee8..aa5ecf479a57 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -183,7 +183,7 @@ static int v9fs_file_do_lock(struct file *filp, int cmd, struct file_lock *fl) else flock.length = fl->fl_end - fl->fl_start + 1; flock.proc_id = fl->fl_pid; - flock.client_id = utsname()->nodename; + flock.client_id = fid->clnt->name; if (IS_SETLKW(cmd)) flock.flags = P9_LOCK_FLAGS_BLOCK; @@ -260,7 +260,7 @@ static int v9fs_file_getlock(struct file *filp, struct file_lock *fl) else glock.length = fl->fl_end - fl->fl_start + 1; glock.proc_id = fl->fl_pid; - glock.client_id = utsname()->nodename; + glock.client_id = fid->clnt->name; res = p9_client_getlock_dotl(fid, &glock); if (res < 0) diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 4c7c01a73911..c38a005bd0cf 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -26,6 +26,8 @@ #ifndef NET_9P_CLIENT_H #define NET_9P_CLIENT_H +#include + /* Number of requests per row */ #define P9_ROW_MAXTAG 255 @@ -134,6 +136,7 @@ struct p9_req_t { * @tagpool - transaction id accounting for session * @reqs - 2D array of requests * @max_tag - current maximum tag id allocated + * @name - node name used as client id * * The client structure is used to keep track of various per-client * state that has been instantiated. @@ -164,6 +167,8 @@ struct p9_client { struct p9_idpool *tagpool; struct p9_req_t *reqs[P9_ROW_MAXTAG]; int max_tag; + + char name[__NEW_UTS_LEN + 1]; }; /** diff --git a/net/9p/client.c b/net/9p/client.c index 8b93cae2d11d..0e49b288e574 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -992,6 +992,7 @@ struct p9_client *p9_client_create(const char *dev_name, char *options) { int err; struct p9_client *clnt; + char *client_id; err = 0; clnt = kmalloc(sizeof(struct p9_client), GFP_KERNEL); @@ -1000,6 +1001,10 @@ struct p9_client *p9_client_create(const char *dev_name, char *options) clnt->trans_mod = NULL; clnt->trans = NULL; + + client_id = utsname()->nodename; + memcpy(clnt->name, client_id, strlen(client_id) + 1); + spin_lock_init(&clnt->lock); INIT_LIST_HEAD(&clnt->fidlist); -- cgit v1.2.3-71-gd317 From 6e9e318b304fd7373a0754805a76a02ddbc69a41 Mon Sep 17 00:00:00 2001 From: Haijun Zhang Date: Mon, 26 Aug 2013 09:19:22 +0800 Subject: mmc: core: parse voltage from device-tree Add function to support getting voltage from device-tree. If voltage-range is specified in device-tree node, this function will parse it and return the available voltage mask. Signed-off-by: Haijun Zhang Acked-by: Anton Vorontsov Signed-off-by: Chris Ball --- .../devicetree/bindings/mmc/fsl-esdhc.txt | 4 ++ drivers/mmc/core/core.c | 44 ++++++++++++++++++++++ include/linux/mmc/core.h | 2 + 3 files changed, 50 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt index bd9be0b5bc20..b7943f3f9995 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt +++ b/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt @@ -19,6 +19,9 @@ Optional properties: "bus-width = <1>" property. - sdhci,auto-cmd12: specifies that a controller can only handle auto CMD12. + - voltage-ranges : two cells are required, first cell specifies minimum + slot voltage (mV), second cell specifies maximum slot voltage (mV). + Several ranges could be specified. Example: @@ -29,4 +32,5 @@ sdhci@2e000 { interrupt-parent = <&ipic>; /* Filled in by U-Boot */ clock-frequency = <0>; + voltage-ranges = <3300 3300>; }; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 49a5bca418bd..b9b9fb6e4496 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -1196,6 +1197,49 @@ u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max) } EXPORT_SYMBOL(mmc_vddrange_to_ocrmask); +#ifdef CONFIG_OF + +/** + * mmc_of_parse_voltage - return mask of supported voltages + * @np: The device node need to be parsed. + * @mask: mask of voltages available for MMC/SD/SDIO + * + * 1. Return zero on success. + * 2. Return negative errno: voltage-range is invalid. + */ +int mmc_of_parse_voltage(struct device_node *np, u32 *mask) +{ + const u32 *voltage_ranges; + int num_ranges, i; + + voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges); + num_ranges = num_ranges / sizeof(*voltage_ranges) / 2; + if (!voltage_ranges || !num_ranges) { + pr_info("%s: voltage-ranges unspecified\n", np->full_name); + return -EINVAL; + } + + for (i = 0; i < num_ranges; i++) { + const int j = i * 2; + u32 ocr_mask; + + ocr_mask = mmc_vddrange_to_ocrmask( + be32_to_cpu(voltage_ranges[j]), + be32_to_cpu(voltage_ranges[j + 1])); + if (!ocr_mask) { + pr_err("%s: voltage-range #%d is invalid\n", + np->full_name, i); + return -EINVAL; + } + *mask |= ocr_mask; + } + + return 0; +} +EXPORT_SYMBOL(mmc_of_parse_voltage); + +#endif /* CONFIG_OF */ + #ifdef CONFIG_REGULATOR /** diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 443243b241d5..da51bec578c3 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -208,6 +208,8 @@ static inline void mmc_claim_host(struct mmc_host *host) __mmc_claim_host(host, NULL); } +struct device_node; extern u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max); +extern int mmc_of_parse_voltage(struct device_node *np, u32 *mask); #endif /* LINUX_MMC_CORE_H */ -- cgit v1.2.3-71-gd317 From c0b887b66c95fe5abaac071b8332b8c21113d84b Mon Sep 17 00:00:00 2001 From: Haijun Zhang Date: Mon, 26 Aug 2013 09:19:23 +0800 Subject: mmc: sdhci: get voltage from sdhc host We use host->ocr_mask to hold the voltage get from device-tree node, In case host->ocr_mask was available, we use host->ocr_mask as the final available voltage can be used by MMC/SD/SDIO card. Signed-off-by: Haijun Zhang Reviewed-by: Anton Vorontsov Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 3 +++ include/linux/mmc/sdhci.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 32db0efa2f2c..b4d7f27d966b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3119,6 +3119,9 @@ int sdhci_add_host(struct sdhci_host *host) SDHCI_MAX_CURRENT_MULTIPLIER; } + if (host->ocr_mask) + ocr_avail = host->ocr_mask; + mmc->ocr_avail = ocr_avail; mmc->ocr_avail_sdio = ocr_avail; if (host->ocr_avail_sdio) diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index e3c6a74d980a..3e781b8c0be7 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -171,6 +171,7 @@ struct sdhci_host { unsigned int ocr_avail_sdio; /* OCR bit masks */ unsigned int ocr_avail_sd; unsigned int ocr_avail_mmc; + u32 ocr_mask; /* available voltages */ wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */ -- cgit v1.2.3-71-gd317 From 1ea4c16120f529d811de0a35db6b252352268e95 Mon Sep 17 00:00:00 2001 From: Andrey Moiseev Date: Sun, 25 Aug 2013 22:51:15 -0700 Subject: Input: add driver for slidebar on Lenovo IdeaPad laptops This driver adds support for slidebars found on some Lenovo IdeaPad laptops (the slidebars work with SlideNav/Desktop Navigator under Windows). Fixes: https://bugzilla.kernel.org/show_bug.cgi?id=16004 Registers 'IdeaPad Slidebar' input device and /sys/devices/platform/ideapad_slidebar/slidebar_mode for switching slidebar's modes. Now works on: IdeaPad Y550, Y550P. May work on (testing and adding new models is needed): Ideapad Y560, Y460, Y450, Y650, and, probably, some others. Signed-off-by: Andrey Moiseev Signed-off-by: Dmitry Torokhov --- MAINTAINERS | 7 + drivers/input/misc/Kconfig | 10 + drivers/input/misc/Makefile | 1 + drivers/input/misc/ideapad_slidebar.c | 358 ++++++++++++++++++++++++++++++++++ drivers/input/serio/i8042.h | 24 --- include/linux/i8042.h | 24 +++ 6 files changed, 400 insertions(+), 24 deletions(-) create mode 100644 drivers/input/misc/ideapad_slidebar.c (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 250dc970c62d..bd1c48ebce69 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4038,6 +4038,13 @@ W: http://launchpad.net/ideapad-laptop S: Maintained F: drivers/platform/x86/ideapad-laptop.c +IDEAPAD LAPTOP SLIDEBAR DRIVER +M: Andrey Moiseev +L: linux-input@vger.kernel.org +W: https://github.com/o2genum/ideapad-slidebar +S: Maintained +F: drivers/input/misc/ideapad_slidebar.c + IDE/ATAPI DRIVERS M: Borislav Petkov L: linux-ide@vger.kernel.org diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 0b541cdf9b8e..aa51baaa9b1e 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -647,4 +647,14 @@ config INPUT_SIRFSOC_ONKEY If unsure, say N. +config INPUT_IDEAPAD_SLIDEBAR + tristate "IdeaPad Laptop Slidebar" + depends on INPUT + depends on SERIO_I8042 + help + Say Y here if you have an IdeaPad laptop with a slidebar. + + To compile this driver as a module, choose M here: the + module will be called ideapad_slidebar. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 829de43a2427..0ebfb6dbf0f7 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -61,3 +61,4 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o +obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o diff --git a/drivers/input/misc/ideapad_slidebar.c b/drivers/input/misc/ideapad_slidebar.c new file mode 100644 index 000000000000..edfd6239f131 --- /dev/null +++ b/drivers/input/misc/ideapad_slidebar.c @@ -0,0 +1,358 @@ +/* + * Input driver for slidebars on some Lenovo IdeaPad laptops + * + * Copyright (C) 2013 Andrey Moiseev + * + * Reverse-engineered from Lenovo SlideNav software (SBarHook.dll). + * + * 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; either version 2 of the License, or (at your option) + * any later version. + * + * Trademarks are the property of their respective owners. + */ + +/* + * Currently tested and works on: + * Lenovo IdeaPad Y550 + * Lenovo IdeaPad Y550P + * + * Other models can be added easily. To test, + * load with 'force' parameter set 'true'. + * + * LEDs blinking and input mode are managed via sysfs, + * (hex, unsigned byte value): + * /sys/devices/platform/ideapad_slidebar/slidebar_mode + * + * The value is in byte range, however, I only figured out + * how bits 0b10011001 work. Some other bits, probably, + * are meaningfull too. + * + * Possible states: + * + * STD_INT, ONMOV_INT, OFF_INT, LAST_POLL, OFF_POLL + * + * Meaning: + * released touched + * STD 'heartbeat' lights follow the finger + * ONMOV no lights lights follow the finger + * LAST at last pos lights follow the finger + * OFF no lights no lights + * + * INT all input events are generated, interrupts are used + * POLL no input events by default, to get them, + * send 0b10000000 (read below) + * + * Commands: write + * + * All | 0b01001 -> STD_INT + * possible | 0b10001 -> ONMOV_INT + * states | 0b01000 -> OFF_INT + * + * | 0b0 -> LAST_POLL + * STD_INT or ONMOV_INT | + * | 0b1 -> STD_INT + * + * | 0b0 -> OFF_POLL + * OFF_INT or OFF_POLL | + * | 0b1 -> OFF_INT + * + * Any state | 0b10000000 -> if the slidebar has updated data, + * produce one input event (last position), + * switch to respective POLL mode + * (like 0x0), if not in POLL mode yet. + * + * Get current state: read + * + * masked by 0x11 read value means: + * + * 0x00 LAST + * 0x01 STD + * 0x10 OFF + * 0x11 ONMOV + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IDEAPAD_BASE 0xff29 + +static bool force; +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); + +static DEFINE_SPINLOCK(io_lock); + +static struct input_dev *slidebar_input_dev; +static struct platform_device *slidebar_platform_dev; + +static u8 slidebar_pos_get(void) +{ + u8 res; + unsigned long flags; + + spin_lock_irqsave(&io_lock, flags); + outb(0xf4, 0xff29); + outb(0xbf, 0xff2a); + res = inb(0xff2b); + spin_unlock_irqrestore(&io_lock, flags); + + return res; +} + +static u8 slidebar_mode_get(void) +{ + u8 res; + unsigned long flags; + + spin_lock_irqsave(&io_lock, flags); + outb(0xf7, 0xff29); + outb(0x8b, 0xff2a); + res = inb(0xff2b); + spin_unlock_irqrestore(&io_lock, flags); + + return res; +} + +static void slidebar_mode_set(u8 mode) +{ + unsigned long flags; + + spin_lock_irqsave(&io_lock, flags); + outb(0xf7, 0xff29); + outb(0x8b, 0xff2a); + outb(mode, 0xff2b); + spin_unlock_irqrestore(&io_lock, flags); +} + +static bool slidebar_i8042_filter(unsigned char data, unsigned char str, + struct serio *port) +{ + static bool extended = false; + + /* We are only interested in data coming form KBC port */ + if (str & I8042_STR_AUXDATA) + return false; + + /* Scancodes: e03b on move, e0bb on release. */ + if (data == 0xe0) { + extended = true; + return true; + } + + if (!extended) + return false; + + extended = false; + + if (likely((data & 0x7f) != 0x3b)) { + serio_interrupt(port, 0xe0, 0); + return false; + } + + if (data & 0x80) { + input_report_key(slidebar_input_dev, BTN_TOUCH, 0); + } else { + input_report_key(slidebar_input_dev, BTN_TOUCH, 1); + input_report_abs(slidebar_input_dev, ABS_X, slidebar_pos_get()); + } + input_sync(slidebar_input_dev); + + return true; +} + +static ssize_t show_slidebar_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%x\n", slidebar_mode_get()); +} + +static ssize_t store_slidebar_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u8 mode; + int error; + + error = kstrtou8(buf, 0, &mode); + if (error) + return error; + + slidebar_mode_set(mode); + + return count; +} + +static DEVICE_ATTR(slidebar_mode, S_IWUSR | S_IRUGO, + show_slidebar_mode, store_slidebar_mode); + +static struct attribute *ideapad_attrs[] = { + &dev_attr_slidebar_mode.attr, + NULL +}; + +static struct attribute_group ideapad_attr_group = { + .attrs = ideapad_attrs +}; + +static const struct attribute_group *ideapad_attr_groups[] = { + &ideapad_attr_group, + NULL +}; + +static int __init ideapad_probe(struct platform_device* pdev) +{ + int err; + + if (!request_region(IDEAPAD_BASE, 3, "ideapad_slidebar")) { + dev_err(&pdev->dev, "IO ports are busy\n"); + return -EBUSY; + } + + slidebar_input_dev = input_allocate_device(); + if (!slidebar_input_dev) { + dev_err(&pdev->dev, "Failed to allocate input device\n"); + err = -ENOMEM; + goto err_release_ports; + } + + slidebar_input_dev->name = "IdeaPad Slidebar"; + slidebar_input_dev->id.bustype = BUS_HOST; + slidebar_input_dev->dev.parent = &pdev->dev; + input_set_capability(slidebar_input_dev, EV_KEY, BTN_TOUCH); + input_set_capability(slidebar_input_dev, EV_ABS, ABS_X); + input_set_abs_params(slidebar_input_dev, ABS_X, 0, 0xff, 0, 0); + + err = i8042_install_filter(slidebar_i8042_filter); + if (err) { + dev_err(&pdev->dev, + "Failed to install i8042 filter: %d\n", err); + goto err_free_dev; + } + + err = input_register_device(slidebar_input_dev); + if (err) { + dev_err(&pdev->dev, + "Failed to register input device: %d\n", err); + goto err_remove_filter; + } + + return 0; + +err_remove_filter: + i8042_remove_filter(slidebar_i8042_filter); +err_free_dev: + input_free_device(slidebar_input_dev); +err_release_ports: + release_region(IDEAPAD_BASE, 3); + return err; +} + +static int ideapad_remove(struct platform_device *pdev) +{ + i8042_remove_filter(slidebar_i8042_filter); + input_unregister_device(slidebar_input_dev); + release_region(IDEAPAD_BASE, 3); + + return 0; +} + +static struct platform_driver slidebar_drv = { + .driver = { + .name = "ideapad_slidebar", + .owner = THIS_MODULE, + }, + .remove = ideapad_remove, +}; + +static int __init ideapad_dmi_check(const struct dmi_system_id *id) +{ + pr_info("Laptop model '%s'\n", id->ident); + return 1; +} + +static const struct dmi_system_id ideapad_dmi[] __initconst = { + { + .ident = "Lenovo IdeaPad Y550", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20017"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo IdeaPad Y550") + }, + .callback = ideapad_dmi_check + }, + { + .ident = "Lenovo IdeaPad Y550P", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20035"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo IdeaPad Y550P") + }, + .callback = ideapad_dmi_check + }, + { NULL, } +}; +MODULE_DEVICE_TABLE(dmi, ideapad_dmi); + +static int __init slidebar_init(void) +{ + int err; + + if (!force && !dmi_check_system(ideapad_dmi)) { + pr_err("DMI does not match\n"); + return -ENODEV; + } + + slidebar_platform_dev = platform_device_alloc("ideapad_slidebar", -1); + if (!slidebar_platform_dev) { + pr_err("Not enough memory\n"); + return -ENOMEM; + } + + slidebar_platform_dev->dev.groups = ideapad_attr_groups; + + err = platform_device_add(slidebar_platform_dev); + if (err) { + pr_err("Failed to register platform device\n"); + goto err_free_dev; + } + + err = platform_driver_probe(&slidebar_drv, ideapad_probe); + if (err) { + pr_err("Failed to register platform driver\n"); + goto err_delete_dev; + } + + return 0; + +err_delete_dev: + platform_device_del(slidebar_platform_dev); +err_free_dev: + platform_device_put(slidebar_platform_dev); + return err; +} + +static void __exit slidebar_exit(void) +{ + platform_device_unregister(slidebar_platform_dev); + platform_driver_unregister(&slidebar_drv); +} + +module_init(slidebar_init); +module_exit(slidebar_exit); + +MODULE_AUTHOR("Andrey Moiseev "); +MODULE_DESCRIPTION("Slidebar input support for some Lenovo IdeaPad laptops"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h index 3452708fbe3b..fc080beffedc 100644 --- a/drivers/input/serio/i8042.h +++ b/drivers/input/serio/i8042.h @@ -40,30 +40,6 @@ #define I8042_CTL_TIMEOUT 10000 -/* - * Status register bits. - */ - -#define I8042_STR_PARITY 0x80 -#define I8042_STR_TIMEOUT 0x40 -#define I8042_STR_AUXDATA 0x20 -#define I8042_STR_KEYLOCK 0x10 -#define I8042_STR_CMDDAT 0x08 -#define I8042_STR_MUXERR 0x04 -#define I8042_STR_IBF 0x02 -#define I8042_STR_OBF 0x01 - -/* - * Control register bits. - */ - -#define I8042_CTR_KBDINT 0x01 -#define I8042_CTR_AUXINT 0x02 -#define I8042_CTR_IGNKEYLOCK 0x08 -#define I8042_CTR_KBDDIS 0x10 -#define I8042_CTR_AUXDIS 0x20 -#define I8042_CTR_XLATE 0x40 - /* * Return codes. */ diff --git a/include/linux/i8042.h b/include/linux/i8042.h index a986ff588944..0f9bafa17a02 100644 --- a/include/linux/i8042.h +++ b/include/linux/i8042.h @@ -31,6 +31,30 @@ #define I8042_CMD_MUX_PFX 0x0090 #define I8042_CMD_MUX_SEND 0x1090 +/* + * Status register bits. + */ + +#define I8042_STR_PARITY 0x80 +#define I8042_STR_TIMEOUT 0x40 +#define I8042_STR_AUXDATA 0x20 +#define I8042_STR_KEYLOCK 0x10 +#define I8042_STR_CMDDAT 0x08 +#define I8042_STR_MUXERR 0x04 +#define I8042_STR_IBF 0x02 +#define I8042_STR_OBF 0x01 + +/* + * Control register bits. + */ + +#define I8042_CTR_KBDINT 0x01 +#define I8042_CTR_AUXINT 0x02 +#define I8042_CTR_IGNKEYLOCK 0x08 +#define I8042_CTR_KBDDIS 0x10 +#define I8042_CTR_AUXDIS 0x20 +#define I8042_CTR_XLATE 0x40 + struct serio; #if defined(CONFIG_SERIO_I8042) || defined(CONFIG_SERIO_I8042_MODULE) -- cgit v1.2.3-71-gd317 From cfe51ec1ae427ec0be5a7670eae815ce5eb30e1c Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 24 Aug 2013 00:32:30 +0200 Subject: bcma: add method to power up and down the PCIe core by wifi driver The wifi driver should tell the PCIe core that it is now in operation so that some workarounds can be applied and the power state is changed. This should replace the call to bcma_core_pci_extend_L1timer by the brcmsmac driver. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_pci.c | 26 ++++++++++++++++++++++++++ include/linux/bcma/bcma_driver_pci.h | 3 +++ 2 files changed, 29 insertions(+) (limited to 'include') diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index cf7a476a519f..6ea817fae59f 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -275,3 +275,29 @@ void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG); } EXPORT_SYMBOL_GPL(bcma_core_pci_extend_L1timer); + +void bcma_core_pci_up(struct bcma_bus *bus) +{ + struct bcma_drv_pci *pc; + + if (bus->hosttype != BCMA_HOSTTYPE_PCI) + return; + + pc = &bus->drv_pci[0]; + + bcma_core_pci_extend_L1timer(pc, true); +} +EXPORT_SYMBOL_GPL(bcma_core_pci_up); + +void bcma_core_pci_down(struct bcma_bus *bus) +{ + struct bcma_drv_pci *pc; + + if (bus->hosttype != BCMA_HOSTTYPE_PCI) + return; + + pc = &bus->drv_pci[0]; + + bcma_core_pci_extend_L1timer(pc, false); +} +EXPORT_SYMBOL_GPL(bcma_core_pci_down); diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 424760f01b9d..27f9ceb68b42 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -185,6 +185,7 @@ struct pci_dev; #define BCMA_CORE_PCI_RC_CRS_VISIBILITY 0x0001 struct bcma_drv_pci; +struct bcma_bus; #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE struct bcma_drv_pci_host { @@ -220,6 +221,8 @@ extern void bcma_core_pci_init(struct bcma_drv_pci *pc); extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core, bool enable); extern void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend); +extern void bcma_core_pci_up(struct bcma_bus *bus); +extern void bcma_core_pci_down(struct bcma_bus *bus); extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev); extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev); -- cgit v1.2.3-71-gd317 From 780335acc815802dcee63d75f5589d43c3ccb402 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 24 Aug 2013 00:32:32 +0200 Subject: bcma: do not export bcma_core_pci_extend_L1timer() This is not called any more, do not export it. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_pci.c | 3 +-- include/linux/bcma/bcma_driver_pci.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index 6ea817fae59f..d51d1943a202 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -262,7 +262,7 @@ out: } EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl); -void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) +static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) { u32 w; @@ -274,7 +274,6 @@ void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) bcma_pcie_write(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG, w); bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG); } -EXPORT_SYMBOL_GPL(bcma_core_pci_extend_L1timer); void bcma_core_pci_up(struct bcma_bus *bus) { diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 27f9ceb68b42..0234955aa9c7 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -220,7 +220,6 @@ struct bcma_drv_pci { extern void bcma_core_pci_init(struct bcma_drv_pci *pc); extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core, bool enable); -extern void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend); extern void bcma_core_pci_up(struct bcma_bus *bus); extern void bcma_core_pci_down(struct bcma_bus *bus); -- cgit v1.2.3-71-gd317 From 521deea64088bc885a76bd174241eaa3d3a6876f Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 24 Aug 2013 00:32:33 +0200 Subject: bcma: add bcma_core_pci_power_save() This enables or disables power saving on the PCIe bus when the wifi is in operation or not. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/bcma/driver_pci.c | 36 +++++++++++++++++++++++++++++++++--- include/linux/bcma/bcma_driver_pci.h | 20 ++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index d51d1943a202..c9fd6943ce45 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -31,7 +31,7 @@ static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data) pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data); } -static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy) +static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u16 phy) { u32 v; int i; @@ -55,7 +55,7 @@ static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy) } } -static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address) +static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u16 device, u8 address) { int max_retries = 10; u16 ret = 0; @@ -98,7 +98,7 @@ static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address) return ret; } -static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device, +static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u16 device, u8 address, u16 data) { int max_retries = 10; @@ -137,6 +137,13 @@ static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device, pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0); } +static u16 bcma_pcie_mdio_writeread(struct bcma_drv_pci *pc, u16 device, + u8 address, u16 data) +{ + bcma_pcie_mdio_write(pc, device, address, data); + return bcma_pcie_mdio_read(pc, device, address); +} + /************************************************** * Workarounds. **************************************************/ @@ -203,6 +210,25 @@ static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc) } } +static void bcma_core_pci_power_save(struct bcma_drv_pci *pc, bool up) +{ + u16 data; + + if (pc->core->id.rev >= 15 && pc->core->id.rev <= 20) { + data = up ? 0x74 : 0x7C; + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7F64); + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data); + } else if (pc->core->id.rev >= 21 && pc->core->id.rev <= 22) { + data = up ? 0x75 : 0x7D; + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7E65); + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data); + } +} + /************************************************** * Init. **************************************************/ @@ -284,6 +310,8 @@ void bcma_core_pci_up(struct bcma_bus *bus) pc = &bus->drv_pci[0]; + bcma_core_pci_power_save(pc, true); + bcma_core_pci_extend_L1timer(pc, true); } EXPORT_SYMBOL_GPL(bcma_core_pci_up); @@ -298,5 +326,7 @@ void bcma_core_pci_down(struct bcma_bus *bus) pc = &bus->drv_pci[0]; bcma_core_pci_extend_L1timer(pc, false); + + bcma_core_pci_power_save(pc, false); } EXPORT_SYMBOL_GPL(bcma_core_pci_down); diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h index 0234955aa9c7..d66033f418c9 100644 --- a/include/linux/bcma/bcma_driver_pci.h +++ b/include/linux/bcma/bcma_driver_pci.h @@ -181,6 +181,26 @@ struct pci_dev; #define BCMA_CORE_PCI_CFG_DEVCTRL 0xd8 +#define BCMA_CORE_PCI_ + +/* MDIO devices (SERDES modules) */ +#define BCMA_CORE_PCI_MDIO_IEEE0 0x000 +#define BCMA_CORE_PCI_MDIO_IEEE1 0x001 +#define BCMA_CORE_PCI_MDIO_BLK0 0x800 +#define BCMA_CORE_PCI_MDIO_BLK1 0x801 +#define BCMA_CORE_PCI_MDIO_BLK1_MGMT0 0x16 +#define BCMA_CORE_PCI_MDIO_BLK1_MGMT1 0x17 +#define BCMA_CORE_PCI_MDIO_BLK1_MGMT2 0x18 +#define BCMA_CORE_PCI_MDIO_BLK1_MGMT3 0x19 +#define BCMA_CORE_PCI_MDIO_BLK1_MGMT4 0x1A +#define BCMA_CORE_PCI_MDIO_BLK2 0x802 +#define BCMA_CORE_PCI_MDIO_BLK3 0x803 +#define BCMA_CORE_PCI_MDIO_BLK4 0x804 +#define BCMA_CORE_PCI_MDIO_TXPLL 0x808 /* TXPLL register block idx */ +#define BCMA_CORE_PCI_MDIO_TXCTRL0 0x820 +#define BCMA_CORE_PCI_MDIO_SERDESID 0x831 +#define BCMA_CORE_PCI_MDIO_RXCTRL0 0x840 + /* PCIE Root Capability Register bits (Host mode only) */ #define BCMA_CORE_PCI_RC_CRS_VISIBILITY 0x0001 -- cgit v1.2.3-71-gd317 From 277fe44dd862412ee034470ad1c13a79d24e533b Mon Sep 17 00:00:00 2001 From: Yonghua Zheng Date: Mon, 26 Aug 2013 23:38:35 +0800 Subject: HID: hidraw: Add spinlock in struct hidraw to protect list It is unsafe to call list_for_each_entry in hidraw_report_event to traverse each hidraw_list node without a lock protection, the list could be modified if someone calls hidraw_release and list_del to remove itself from the list, this can cause hidraw_report_event to touch a deleted list struct and panic. To prevent this, introduce a spinlock in struct hidraw to protect list from concurrent access. Signed-off-by: Yonghua Zheng Signed-off-by: Jiri Kosina --- drivers/hid/hidraw.c | 20 +++++++++++++++----- include/linux/hidraw.h | 1 + 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 612a655bc9f0..194a5660a389 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -253,6 +253,7 @@ static int hidraw_open(struct inode *inode, struct file *file) unsigned int minor = iminor(inode); struct hidraw *dev; struct hidraw_list *list; + unsigned long flags; int err = 0; if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) { @@ -266,11 +267,6 @@ static int hidraw_open(struct inode *inode, struct file *file) goto out_unlock; } - list->hidraw = hidraw_table[minor]; - mutex_init(&list->read_mutex); - list_add_tail(&list->node, &hidraw_table[minor]->list); - file->private_data = list; - dev = hidraw_table[minor]; if (!dev->open++) { err = hid_hw_power(dev->hid, PM_HINT_FULLON); @@ -283,9 +279,16 @@ static int hidraw_open(struct inode *inode, struct file *file) if (err < 0) { hid_hw_power(dev->hid, PM_HINT_NORMAL); dev->open--; + goto out_unlock; } } + list->hidraw = hidraw_table[minor]; + mutex_init(&list->read_mutex); + spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags); + list_add_tail(&list->node, &hidraw_table[minor]->list); + spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); + file->private_data = list; out_unlock: mutex_unlock(&minors_lock); out: @@ -324,10 +327,13 @@ static int hidraw_release(struct inode * inode, struct file * file) { unsigned int minor = iminor(inode); struct hidraw_list *list = file->private_data; + unsigned long flags; mutex_lock(&minors_lock); + spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags); list_del(&list->node); + spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); kfree(list); drop_ref(hidraw_table[minor], 0); @@ -456,7 +462,9 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len) struct hidraw *dev = hid->hidraw; struct hidraw_list *list; int ret = 0; + unsigned long flags; + spin_lock_irqsave(&dev->list_lock, flags); list_for_each_entry(list, &dev->list, node) { int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); @@ -471,6 +479,7 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len) list->head = new_head; kill_fasync(&list->fasync, SIGIO, POLL_IN); } + spin_unlock_irqrestore(&dev->list_lock, flags); wake_up_interruptible(&dev->wait); return ret; @@ -519,6 +528,7 @@ int hidraw_connect(struct hid_device *hid) mutex_unlock(&minors_lock); init_waitqueue_head(&dev->wait); + spin_lock_init(&dev->list_lock); INIT_LIST_HEAD(&dev->list); dev->hid = hid; diff --git a/include/linux/hidraw.h b/include/linux/hidraw.h index 2451662c728a..ddf52612eed8 100644 --- a/include/linux/hidraw.h +++ b/include/linux/hidraw.h @@ -23,6 +23,7 @@ struct hidraw { wait_queue_head_t wait; struct hid_device *hid; struct device *dev; + spinlock_t list_lock; struct list_head list; }; -- cgit v1.2.3-71-gd317 From a175a723301a8a4a9fedf9ce5b8ca586e7a97b40 Mon Sep 17 00:00:00 2001 From: Joe Stringer Date: Thu, 22 Aug 2013 12:30:48 -0700 Subject: openvswitch: Add SCTP support This patch adds support for rewriting SCTP src,dst ports similar to the functionality already available for TCP/UDP. Rewriting SCTP ports is expensive due to double-recalculation of the SCTP checksums; this is performed to ensure that packets traversing OVS with invalid checksums will continue to the destination with any checksum corruption intact. Reviewed-by: Simon Horman Signed-off-by: Joe Stringer Signed-off-by: Ben Pfaff Signed-off-by: Jesse Gross --- include/uapi/linux/openvswitch.h | 6 ++++ net/openvswitch/Kconfig | 1 + net/openvswitch/actions.c | 39 ++++++++++++++++++++++++ net/openvswitch/datapath.c | 6 ++++ net/openvswitch/flow.c | 65 ++++++++++++++++++++++++++++++++++++++++ net/openvswitch/flow.h | 8 ++--- 6 files changed, 121 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index de1fa5d3780f..a74d375b439b 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -259,6 +259,7 @@ enum ovs_key_attr { OVS_KEY_ATTR_ND, /* struct ovs_key_nd */ OVS_KEY_ATTR_SKB_MARK, /* u32 skb mark */ OVS_KEY_ATTR_TUNNEL, /* Nested set of ovs_tunnel attributes */ + OVS_KEY_ATTR_SCTP, /* struct ovs_key_sctp */ #ifdef __KERNEL__ OVS_KEY_ATTR_IPV4_TUNNEL, /* struct ovs_key_ipv4_tunnel */ @@ -333,6 +334,11 @@ struct ovs_key_udp { __be16 udp_dst; }; +struct ovs_key_sctp { + __be16 sctp_src; + __be16 sctp_dst; +}; + struct ovs_key_icmp { __u8 icmp_type; __u8 icmp_code; diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig index bed30e69baa7..6ecf491ad509 100644 --- a/net/openvswitch/Kconfig +++ b/net/openvswitch/Kconfig @@ -4,6 +4,7 @@ config OPENVSWITCH tristate "Open vSwitch" + select LIBCRC32C ---help--- Open vSwitch is a multilayer Ethernet switch targeted at virtualized environments. In addition to supporting a variety of features diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 1f680222f4f7..65cfaa816075 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include #include "datapath.h" #include "vport.h" @@ -352,6 +354,39 @@ static int set_tcp(struct sk_buff *skb, const struct ovs_key_tcp *tcp_port_key) return 0; } +static int set_sctp(struct sk_buff *skb, + const struct ovs_key_sctp *sctp_port_key) +{ + struct sctphdr *sh; + int err; + unsigned int sctphoff = skb_transport_offset(skb); + + err = make_writable(skb, sctphoff + sizeof(struct sctphdr)); + if (unlikely(err)) + return err; + + sh = sctp_hdr(skb); + if (sctp_port_key->sctp_src != sh->source || + sctp_port_key->sctp_dst != sh->dest) { + __le32 old_correct_csum, new_csum, old_csum; + + old_csum = sh->checksum; + old_correct_csum = sctp_compute_cksum(skb, sctphoff); + + sh->source = sctp_port_key->sctp_src; + sh->dest = sctp_port_key->sctp_dst; + + new_csum = sctp_compute_cksum(skb, sctphoff); + + /* Carry any checksum errors through. */ + sh->checksum = old_csum ^ old_correct_csum ^ new_csum; + + skb->rxhash = 0; + } + + return 0; +} + static int do_output(struct datapath *dp, struct sk_buff *skb, int out_port) { struct vport *vport; @@ -461,6 +496,10 @@ static int execute_set_action(struct sk_buff *skb, case OVS_KEY_ATTR_UDP: err = set_udp(skb, nla_data(nested_attr)); break; + + case OVS_KEY_ATTR_SCTP: + err = set_sctp(skb, nla_data(nested_attr)); + break; } return err; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index d29cd9aa4a67..2aa13bd7f2b2 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -712,6 +712,12 @@ static int validate_set(const struct nlattr *a, return validate_tp_port(flow_key); + case OVS_KEY_ATTR_SCTP: + if (flow_key->ip.proto != IPPROTO_SCTP) + return -EINVAL; + + return validate_tp_port(flow_key); + default: return -EINVAL; } diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 1fceb9653598..2b4785590b56 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -129,6 +130,7 @@ static bool ovs_match_validate(const struct sw_flow_match *match, | (1 << OVS_KEY_ATTR_IPV6) | (1 << OVS_KEY_ATTR_TCP) | (1 << OVS_KEY_ATTR_UDP) + | (1 << OVS_KEY_ATTR_SCTP) | (1 << OVS_KEY_ATTR_ICMP) | (1 << OVS_KEY_ATTR_ICMPV6) | (1 << OVS_KEY_ATTR_ARP) @@ -159,6 +161,12 @@ static bool ovs_match_validate(const struct sw_flow_match *match, mask_allowed |= 1 << OVS_KEY_ATTR_UDP; } + if (match->key->ip.proto == IPPROTO_SCTP) { + key_expected |= 1 << OVS_KEY_ATTR_SCTP; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_SCTP; + } + if (match->key->ip.proto == IPPROTO_TCP) { key_expected |= 1 << OVS_KEY_ATTR_TCP; if (match->mask && (match->mask->key.ip.proto == 0xff)) @@ -185,6 +193,12 @@ static bool ovs_match_validate(const struct sw_flow_match *match, mask_allowed |= 1 << OVS_KEY_ATTR_UDP; } + if (match->key->ip.proto == IPPROTO_SCTP) { + key_expected |= 1 << OVS_KEY_ATTR_SCTP; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_SCTP; + } + if (match->key->ip.proto == IPPROTO_TCP) { key_expected |= 1 << OVS_KEY_ATTR_TCP; if (match->mask && (match->mask->key.ip.proto == 0xff)) @@ -280,6 +294,12 @@ static bool udphdr_ok(struct sk_buff *skb) sizeof(struct udphdr)); } +static bool sctphdr_ok(struct sk_buff *skb) +{ + return pskb_may_pull(skb, skb_transport_offset(skb) + + sizeof(struct sctphdr)); +} + static bool icmphdr_ok(struct sk_buff *skb) { return pskb_may_pull(skb, skb_transport_offset(skb) + @@ -891,6 +911,12 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) key->ipv4.tp.src = udp->source; key->ipv4.tp.dst = udp->dest; } + } else if (key->ip.proto == IPPROTO_SCTP) { + if (sctphdr_ok(skb)) { + struct sctphdr *sctp = sctp_hdr(skb); + key->ipv4.tp.src = sctp->source; + key->ipv4.tp.dst = sctp->dest; + } } else if (key->ip.proto == IPPROTO_ICMP) { if (icmphdr_ok(skb)) { struct icmphdr *icmp = icmp_hdr(skb); @@ -953,6 +979,12 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) key->ipv6.tp.src = udp->source; key->ipv6.tp.dst = udp->dest; } + } else if (key->ip.proto == NEXTHDR_SCTP) { + if (sctphdr_ok(skb)) { + struct sctphdr *sctp = sctp_hdr(skb); + key->ipv6.tp.src = sctp->source; + key->ipv6.tp.dst = sctp->dest; + } } else if (key->ip.proto == NEXTHDR_ICMP) { if (icmp6hdr_ok(skb)) { error = parse_icmpv6(skb, key, nh_len); @@ -1087,6 +1119,7 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_IPV6] = sizeof(struct ovs_key_ipv6), [OVS_KEY_ATTR_TCP] = sizeof(struct ovs_key_tcp), [OVS_KEY_ATTR_UDP] = sizeof(struct ovs_key_udp), + [OVS_KEY_ATTR_SCTP] = sizeof(struct ovs_key_sctp), [OVS_KEY_ATTR_ICMP] = sizeof(struct ovs_key_icmp), [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6), [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp), @@ -1500,6 +1533,24 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, attrs &= ~(1 << OVS_KEY_ATTR_UDP); } + if (attrs & (1 << OVS_KEY_ATTR_SCTP)) { + const struct ovs_key_sctp *sctp_key; + + sctp_key = nla_data(a[OVS_KEY_ATTR_SCTP]); + if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { + SW_FLOW_KEY_PUT(match, ipv4.tp.src, + sctp_key->sctp_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv4.tp.dst, + sctp_key->sctp_dst, is_mask); + } else { + SW_FLOW_KEY_PUT(match, ipv6.tp.src, + sctp_key->sctp_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv6.tp.dst, + sctp_key->sctp_dst, is_mask); + } + attrs &= ~(1 << OVS_KEY_ATTR_SCTP); + } + if (attrs & (1 << OVS_KEY_ATTR_ICMP)) { const struct ovs_key_icmp *icmp_key; @@ -1843,6 +1894,20 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, udp_key->udp_src = output->ipv6.tp.src; udp_key->udp_dst = output->ipv6.tp.dst; } + } else if (swkey->ip.proto == IPPROTO_SCTP) { + struct ovs_key_sctp *sctp_key; + + nla = nla_reserve(skb, OVS_KEY_ATTR_SCTP, sizeof(*sctp_key)); + if (!nla) + goto nla_put_failure; + sctp_key = nla_data(nla); + if (swkey->eth.type == htons(ETH_P_IP)) { + sctp_key->sctp_src = swkey->ipv4.tp.src; + sctp_key->sctp_dst = swkey->ipv4.tp.dst; + } else if (swkey->eth.type == htons(ETH_P_IPV6)) { + sctp_key->sctp_src = swkey->ipv6.tp.src; + sctp_key->sctp_dst = swkey->ipv6.tp.dst; + } } else if (swkey->eth.type == htons(ETH_P_IP) && swkey->ip.proto == IPPROTO_ICMP) { struct ovs_key_icmp *icmp_key; diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index 9674e45f6969..d08dcf78dbf3 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -99,8 +99,8 @@ struct sw_flow_key { } addr; union { struct { - __be16 src; /* TCP/UDP source port. */ - __be16 dst; /* TCP/UDP destination port. */ + __be16 src; /* TCP/UDP/SCTP source port. */ + __be16 dst; /* TCP/UDP/SCTP destination port. */ } tp; struct { u8 sha[ETH_ALEN]; /* ARP source hardware address. */ @@ -115,8 +115,8 @@ struct sw_flow_key { } addr; __be32 label; /* IPv6 flow label. */ struct { - __be16 src; /* TCP/UDP source port. */ - __be16 dst; /* TCP/UDP destination port. */ + __be16 src; /* TCP/UDP/SCTP source port. */ + __be16 dst; /* TCP/UDP/SCTP destination port. */ } tp; struct { struct in6_addr target; /* ND target address. */ -- cgit v1.2.3-71-gd317 From 35cf083619da5677f83e9a8eae813f0b413d7082 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 26 Aug 2013 18:40:56 -0400 Subject: cgroup: rename cgroup_css_from_dir() to css_from_dir() and update its syntax cgroup_css_from_dir() will grow another user. In preparation, make the following changes. * All css functions are prefixed with just "css_", rename it to css_from_dir(). * Take dentry * instead of file * as dentry is what ultimately identifies a cgroup and file may not always be available. Note that the function now checkes whether @dentry->d_inode is NULL as the caller now may specify a negative dentry. * Make it take cgroup_subsys * instead of integer subsys_id. This simplifies the function and allows specifying no subsystem for cgroup->dummy_css. * Make return section a bit less verbose. This patch doesn't introduce any behavior changes. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Kirill A. Shutemov Cc: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar --- include/linux/cgroup.h | 3 ++- kernel/cgroup.c | 26 ++++++++++---------------- kernel/events/core.c | 2 +- 3 files changed, 13 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index b685955d4b29..21ba29869eb8 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -903,7 +903,8 @@ bool css_is_ancestor(struct cgroup_subsys_state *cg, /* Get id and depth of css */ unsigned short css_id(struct cgroup_subsys_state *css); -struct cgroup_subsys_state *cgroup_css_from_dir(struct file *f, int id); +struct cgroup_subsys_state *css_from_dir(struct dentry *dentry, + struct cgroup_subsys *ss); #else /* !CONFIG_CGROUPS */ diff --git a/kernel/cgroup.c b/kernel/cgroup.c index ef43e3f453ef..921b1387c944 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -5700,34 +5700,28 @@ struct cgroup_subsys_state *css_lookup(struct cgroup_subsys *ss, int id) EXPORT_SYMBOL_GPL(css_lookup); /** - * cgroup_css_from_dir - get corresponding css from file open on cgroup dir - * @f: directory file of interest - * @id: subsystem id of interest + * css_from_dir - get corresponding css from the dentry of a cgroup dir + * @dentry: directory dentry of interest + * @ss: subsystem of interest * * Must be called under RCU read lock. The caller is responsible for * pinning the returned css if it needs to be accessed outside the RCU * critical section. */ -struct cgroup_subsys_state *cgroup_css_from_dir(struct file *f, int id) +struct cgroup_subsys_state *css_from_dir(struct dentry *dentry, + struct cgroup_subsys *ss) { struct cgroup *cgrp; - struct inode *inode; - struct cgroup_subsys_state *css; WARN_ON_ONCE(!rcu_read_lock_held()); - inode = file_inode(f); - /* check in cgroup filesystem dir */ - if (inode->i_op != &cgroup_dir_inode_operations) + /* is @dentry a cgroup dir? */ + if (!dentry->d_inode || + dentry->d_inode->i_op != &cgroup_dir_inode_operations) return ERR_PTR(-EBADF); - if (id < 0 || id >= CGROUP_SUBSYS_COUNT) - return ERR_PTR(-EINVAL); - - /* get cgroup */ - cgrp = __d_cgrp(f->f_dentry); - css = cgroup_css(cgrp, id); - return css ? css : ERR_PTR(-ENOENT); + cgrp = __d_cgrp(dentry); + return cgroup_css(cgrp, ss->subsys_id) ?: ERR_PTR(-ENOENT); } /** diff --git a/kernel/events/core.c b/kernel/events/core.c index 23261f957713..b59ab6632f30 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -593,7 +593,7 @@ static inline int perf_cgroup_connect(int fd, struct perf_event *event, rcu_read_lock(); - css = cgroup_css_from_dir(f.file, perf_subsys_id); + css = css_from_dir(f.file->f_dentry, &perf_subsys); if (IS_ERR(css)) { ret = PTR_ERR(css); goto out; -- cgit v1.2.3-71-gd317 From 9fa4db334c7d9570aec7a5121e84fae99aae1d04 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 26 Aug 2013 18:40:56 -0400 Subject: cgroup: implement CFTYPE_NO_PREFIX When cgroup files are created, cgroup core automatically prepends the name of the subsystem as prefix. This patch adds CFTYPE_NO_ which disables the automatic prefix. This is to work around historical baggages and shouldn't be used for new files. This will be used to move "cgroup.event_control" from cgroup core to memcg. Signed-off-by: Tejun Heo Acked-by: Li Zefan Acked-by: Kirill A. Shutemov Cc: Glauber Costa --- include/linux/cgroup.h | 1 + kernel/cgroup.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 21ba29869eb8..3561d305b1e0 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -411,6 +411,7 @@ enum { CFTYPE_ONLY_ON_ROOT = (1 << 0), /* only create on root cgrp */ CFTYPE_NOT_ON_ROOT = (1 << 1), /* don't create on root cgrp */ CFTYPE_INSANE = (1 << 2), /* don't create if sane_behavior */ + CFTYPE_NO_PREFIX = (1 << 3), /* (DON'T USE FOR NEW FILES) no subsys prefix */ }; #define MAX_CFTYPE_NAME 64 diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 7516668d8325..a41dc87cd07e 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2756,7 +2756,8 @@ static int cgroup_add_file(struct cgroup *cgrp, struct cftype *cft) umode_t mode; char name[MAX_CGROUP_TYPE_NAMELEN + MAX_CFTYPE_NAME + 2] = { 0 }; - if (cft->ss && !(cgrp->root->flags & CGRP_ROOT_NOPREFIX)) { + if (cft->ss && !(cft->flags & CFTYPE_NO_PREFIX) && + !(cgrp->root->flags & CGRP_ROOT_NOPREFIX)) { strcpy(name, cft->ss->name); strcat(name, "."); } -- cgit v1.2.3-71-gd317 From 33b3a561f417ec3e1013999ce8bdb6c055abb1ce Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Tue, 9 Jul 2013 02:11:37 -0700 Subject: leds: support new LP8501 device - another LP55xx common LP8501 can drive up to 9 channels like LP5523. LEDs can be controlled directly via the I2C and programmable engines are supported. LP55xx common driver LP8501 is one of LP55xx family device, so LP55xx common code are used. Chip specific data is defined in the structure, 'lp55xx_device_config'. Differences between LP8501 and LP5523 Different register layout for LED output control and others. LP8501 specific feature for separate output power selection. LP8501 doesn't support external clock detection. Different programming engine data. LP8501 specific feature - output power selection Output channels are selected by power selection - Vout or Vdd. Separate power for VDD1-6 and VDD7-9 are available. It is configurable in the platform data. To support this feature, LP55xx DT structure and header are changed. Device tree binding is updated as well. LED pattern data Example pattern data is updated in the driver documentation. Signed-off-by: Milo Kim Signed-off-by: Bryan Wu --- .../devicetree/bindings/leds/leds-lp55xx.txt | 72 +++- Documentation/leds/leds-lp55xx.txt | 30 +- drivers/leds/Kconfig | 18 +- drivers/leds/Makefile | 1 + drivers/leds/leds-lp55xx-common.c | 3 + drivers/leds/leds-lp8501.c | 410 +++++++++++++++++++++ include/linux/platform_data/leds-lp55xx.h | 10 + 7 files changed, 537 insertions(+), 7 deletions(-) create mode 100644 drivers/leds/leds-lp8501.c (limited to 'include') diff --git a/Documentation/devicetree/bindings/leds/leds-lp55xx.txt b/Documentation/devicetree/bindings/leds/leds-lp55xx.txt index d5176882d8b9..a61727f9a6d1 100644 --- a/Documentation/devicetree/bindings/leds/leds-lp55xx.txt +++ b/Documentation/devicetree/bindings/leds/leds-lp55xx.txt @@ -1,7 +1,7 @@ Binding for TI/National Semiconductor LP55xx Led Drivers Required properties: -- compatible: "national,lp5521" or "national,lp5523" or "ti,lp5562" +- compatible: "national,lp5521" or "national,lp5523" or "ti,lp5562" or "ti,lp8501" - reg: I2C slave address - clock-mode: Input clock mode, (0: automode, 1: internal, 2: external) @@ -11,6 +11,11 @@ Each child has own specific current settings Optional properties: - label: Used for naming LEDs +- pwr-sel: LP8501 specific property. Power selection for output channels. + 0: D1~9 are connected to VDD + 1: D1~6 with VDD, D7~9 with VOUT + 2: D1~6 with VOUT, D7~9 with VDD + 3: D1~9 are connected to VOUT Alternatively, each child can have specific channel name - chan-name: Name of each channel name @@ -145,3 +150,68 @@ lp5562@30 { max-cur = /bits/ 8 <0x60>; }; }; + +example 4) LP8501 +9 channels are defined. The 'pwr-sel' is LP8501 specific property. +Others are same as LP5523. + +lp8501@32 { + compatible = "ti,lp8501"; + reg = <0x32>; + clock-mode = /bits/ 8 <2>; + pwr-sel = /bits/ 8 <3>; /* D1~9 connected to VOUT */ + + chan0 { + chan-name = "d1"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + chan1 { + chan-name = "d2"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + chan2 { + chan-name = "d3"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + chan3 { + chan-name = "d4"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + chan4 { + chan-name = "d5"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + chan5 { + chan-name = "d6"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + chan6 { + chan-name = "d7"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + chan7 { + chan-name = "d8"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; + + chan8 { + chan-name = "d9"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; +}; diff --git a/Documentation/leds/leds-lp55xx.txt b/Documentation/leds/leds-lp55xx.txt index eec8fa2ffe4e..82713ff92eb3 100644 --- a/Documentation/leds/leds-lp55xx.txt +++ b/Documentation/leds/leds-lp55xx.txt @@ -1,11 +1,11 @@ -LP5521/LP5523/LP55231 Common Driver -=================================== +LP5521/LP5523/LP55231/LP5562/LP8501 Common Driver +================================================= Authors: Milo(Woogyom) Kim Description ----------- -LP5521, LP5523/55231 and LP5562 have common features as below. +LP5521, LP5523/55231, LP5562 and LP8501 have common features as below. Register access via the I2C Device initialization/deinitialization @@ -109,6 +109,30 @@ As soon as 'loading' is set to 0, registered callback is called. Inside the callback, the selected engine is loaded and memory is updated. To run programmed pattern, 'run_engine' attribute should be enabled. +The pattern sqeuence of LP8501 is same as LP5523. +However pattern data is specific. +Ex 1) Engine 1 is used +echo 1 > /sys/bus/i2c/devices/xxxx/select_engine +echo 1 > /sys/class/firmware/lp8501/loading +echo "9d0140ff7e0040007e00a001c000" > /sys/class/firmware/lp8501/data +echo 0 > /sys/class/firmware/lp8501/loading +echo 1 > /sys/bus/i2c/devices/xxxx/run_engine + +Ex 2) Engine 2 and 3 are used at the same time +echo 2 > /sys/bus/i2c/devices/xxxx/select_engine +sleep 1 +echo 1 > /sys/class/firmware/lp8501/loading +echo "9d0140ff7e0040007e00a001c000" > /sys/class/firmware/lp8501/data +echo 0 > /sys/class/firmware/lp8501/loading +sleep 1 +echo 3 > /sys/bus/i2c/devices/xxxx/select_engine +sleep 1 +echo 1 > /sys/class/firmware/lp8501/loading +echo "9d0340ff7e0040007e00a001c000" > /sys/class/firmware/lp8501/data +echo 0 > /sys/class/firmware/lp8501/loading +sleep 1 +echo 1 > /sys/class/leds/d1/device/run_engine + ( 'run_engine' and 'firmware_cb' ) The sequence of running the program data is common. But each device has own specific register addresses for commands. diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index e43402dd1dea..77329ce672cb 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -194,11 +194,11 @@ config LEDS_LP3944 module will be called leds-lp3944. config LEDS_LP55XX_COMMON - tristate "Common Driver for TI/National LP5521, LP5523/55231 and LP5562" - depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 + tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501" + depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501 select FW_LOADER help - This option supports common operations for LP5521 and LP5523/55231 + This option supports common operations for LP5521/5523/55231/5562/8501 devices. config LEDS_LP5521 @@ -232,6 +232,18 @@ config LEDS_LP5562 Driver provides direct control via LED class and interface for programming the engines. +config LEDS_LP8501 + tristate "LED Support for TI LP8501 LED driver chip" + depends on LEDS_CLASS && I2C + select LEDS_LP55XX_COMMON + help + If you say yes here you get support for TI LP8501 LED driver. + It is 9 channel chip with programmable engines. + Driver provides direct control via LED class and interface for + programming the engines. + It is similar as LP5523, but output power selection is available. + And register layout and engine program schemes are different. + config LEDS_LP8788 tristate "LED support for the TI LP8788 PMIC" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index ac2897732b02..3013113e74d2 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_LEDS_LP55XX_COMMON) += leds-lp55xx-common.o obj-$(CONFIG_LEDS_LP5521) += leds-lp5521.o obj-$(CONFIG_LEDS_LP5523) += leds-lp5523.o obj-$(CONFIG_LEDS_LP5562) += leds-lp5562.o +obj-$(CONFIG_LEDS_LP8501) += leds-lp8501.o obj-$(CONFIG_LEDS_LP8788) += leds-lp8788.o obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o diff --git a/drivers/leds/leds-lp55xx-common.c b/drivers/leds/leds-lp55xx-common.c index c2fecd4d391c..351825b96f16 100644 --- a/drivers/leds/leds-lp55xx-common.c +++ b/drivers/leds/leds-lp55xx-common.c @@ -593,6 +593,9 @@ int lp55xx_of_populate_pdata(struct device *dev, struct device_node *np) of_property_read_string(np, "label", &pdata->label); of_property_read_u8(np, "clock-mode", &pdata->clock_mode); + /* LP8501 specific */ + of_property_read_u8(np, "pwr-sel", (u8 *)&pdata->pwr_sel); + dev->platform_data = pdata; return 0; diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c new file mode 100644 index 000000000000..4573b9471aaa --- /dev/null +++ b/drivers/leds/leds-lp8501.c @@ -0,0 +1,410 @@ +/* + * TI LP8501 9 channel LED Driver + * + * Copyright (C) 2013 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "leds-lp55xx-common.h" + +#define LP8501_PROGRAM_LENGTH 32 +#define LP8501_MAX_LEDS 9 + +/* Registers */ +#define LP8501_REG_ENABLE 0x00 +#define LP8501_ENABLE BIT(6) +#define LP8501_EXEC_M 0x3F +#define LP8501_EXEC_ENG1_M 0x30 +#define LP8501_EXEC_ENG2_M 0x0C +#define LP8501_EXEC_ENG3_M 0x03 +#define LP8501_RUN_ENG1 0x20 +#define LP8501_RUN_ENG2 0x08 +#define LP8501_RUN_ENG3 0x02 + +#define LP8501_REG_OP_MODE 0x01 +#define LP8501_MODE_ENG1_M 0x30 +#define LP8501_MODE_ENG2_M 0x0C +#define LP8501_MODE_ENG3_M 0x03 +#define LP8501_LOAD_ENG1 0x10 +#define LP8501_LOAD_ENG2 0x04 +#define LP8501_LOAD_ENG3 0x01 + +#define LP8501_REG_PWR_CONFIG 0x05 +#define LP8501_PWR_CONFIG_M 0x03 + +#define LP8501_REG_LED_PWM_BASE 0x16 + +#define LP8501_REG_LED_CURRENT_BASE 0x26 + +#define LP8501_REG_CONFIG 0x36 +#define LP8501_PWM_PSAVE BIT(7) +#define LP8501_AUTO_INC BIT(6) +#define LP8501_PWR_SAVE BIT(5) +#define LP8501_CP_AUTO 0x18 +#define LP8501_INT_CLK BIT(0) +#define LP8501_DEFAULT_CFG \ + (LP8501_PWM_PSAVE | LP8501_AUTO_INC | LP8501_PWR_SAVE | LP8501_CP_AUTO) + +#define LP8501_REG_RESET 0x3D +#define LP8501_RESET 0xFF + +#define LP8501_REG_PROG_PAGE_SEL 0x4F +#define LP8501_PAGE_ENG1 0 +#define LP8501_PAGE_ENG2 1 +#define LP8501_PAGE_ENG3 2 + +#define LP8501_REG_PROG_MEM 0x50 + +#define LP8501_ENG1_IS_LOADING(mode) \ + ((mode & LP8501_MODE_ENG1_M) == LP8501_LOAD_ENG1) +#define LP8501_ENG2_IS_LOADING(mode) \ + ((mode & LP8501_MODE_ENG2_M) == LP8501_LOAD_ENG2) +#define LP8501_ENG3_IS_LOADING(mode) \ + ((mode & LP8501_MODE_ENG3_M) == LP8501_LOAD_ENG3) + +static inline void lp8501_wait_opmode_done(void) +{ + usleep_range(1000, 2000); +} + +static void lp8501_set_led_current(struct lp55xx_led *led, u8 led_current) +{ + led->led_current = led_current; + lp55xx_write(led->chip, LP8501_REG_LED_CURRENT_BASE + led->chan_nr, + led_current); +} + +static int lp8501_post_init_device(struct lp55xx_chip *chip) +{ + int ret; + u8 val = LP8501_DEFAULT_CFG; + + ret = lp55xx_write(chip, LP8501_REG_ENABLE, LP8501_ENABLE); + if (ret) + return ret; + + /* Chip startup time is 500 us, 1 - 2 ms gives some margin */ + usleep_range(1000, 2000); + + if (chip->pdata->clock_mode != LP55XX_CLOCK_EXT) + val |= LP8501_INT_CLK; + + ret = lp55xx_write(chip, LP8501_REG_CONFIG, val); + if (ret) + return ret; + + /* Power selection for each output */ + return lp55xx_update_bits(chip, LP8501_REG_PWR_CONFIG, + LP8501_PWR_CONFIG_M, chip->pdata->pwr_sel); +} + +static void lp8501_load_engine(struct lp55xx_chip *chip) +{ + enum lp55xx_engine_index idx = chip->engine_idx; + u8 mask[] = { + [LP55XX_ENGINE_1] = LP8501_MODE_ENG1_M, + [LP55XX_ENGINE_2] = LP8501_MODE_ENG2_M, + [LP55XX_ENGINE_3] = LP8501_MODE_ENG3_M, + }; + + u8 val[] = { + [LP55XX_ENGINE_1] = LP8501_LOAD_ENG1, + [LP55XX_ENGINE_2] = LP8501_LOAD_ENG2, + [LP55XX_ENGINE_3] = LP8501_LOAD_ENG3, + }; + + u8 page_sel[] = { + [LP55XX_ENGINE_1] = LP8501_PAGE_ENG1, + [LP55XX_ENGINE_2] = LP8501_PAGE_ENG2, + [LP55XX_ENGINE_3] = LP8501_PAGE_ENG3, + }; + + lp55xx_update_bits(chip, LP8501_REG_OP_MODE, mask[idx], val[idx]); + + lp8501_wait_opmode_done(); + + lp55xx_write(chip, LP8501_REG_PROG_PAGE_SEL, page_sel[idx]); +} + +static void lp8501_stop_engine(struct lp55xx_chip *chip) +{ + lp55xx_write(chip, LP8501_REG_OP_MODE, 0); + lp8501_wait_opmode_done(); +} + +static void lp8501_turn_off_channels(struct lp55xx_chip *chip) +{ + int i; + + for (i = 0; i < LP8501_MAX_LEDS; i++) + lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + i, 0); +} + +static void lp8501_run_engine(struct lp55xx_chip *chip, bool start) +{ + int ret; + u8 mode; + u8 exec; + + /* stop engine */ + if (!start) { + lp8501_stop_engine(chip); + lp8501_turn_off_channels(chip); + return; + } + + /* + * To run the engine, + * operation mode and enable register should updated at the same time + */ + + ret = lp55xx_read(chip, LP8501_REG_OP_MODE, &mode); + if (ret) + return; + + ret = lp55xx_read(chip, LP8501_REG_ENABLE, &exec); + if (ret) + return; + + /* change operation mode to RUN only when each engine is loading */ + if (LP8501_ENG1_IS_LOADING(mode)) { + mode = (mode & ~LP8501_MODE_ENG1_M) | LP8501_RUN_ENG1; + exec = (exec & ~LP8501_EXEC_ENG1_M) | LP8501_RUN_ENG1; + } + + if (LP8501_ENG2_IS_LOADING(mode)) { + mode = (mode & ~LP8501_MODE_ENG2_M) | LP8501_RUN_ENG2; + exec = (exec & ~LP8501_EXEC_ENG2_M) | LP8501_RUN_ENG2; + } + + if (LP8501_ENG3_IS_LOADING(mode)) { + mode = (mode & ~LP8501_MODE_ENG3_M) | LP8501_RUN_ENG3; + exec = (exec & ~LP8501_EXEC_ENG3_M) | LP8501_RUN_ENG3; + } + + lp55xx_write(chip, LP8501_REG_OP_MODE, mode); + lp8501_wait_opmode_done(); + + lp55xx_update_bits(chip, LP8501_REG_ENABLE, LP8501_EXEC_M, exec); +} + +static int lp8501_update_program_memory(struct lp55xx_chip *chip, + const u8 *data, size_t size) +{ + u8 pattern[LP8501_PROGRAM_LENGTH] = {0}; + unsigned cmd; + char c[3]; + int update_size; + int nrchars; + int offset = 0; + int ret; + int i; + + /* clear program memory before updating */ + for (i = 0; i < LP8501_PROGRAM_LENGTH; i++) + lp55xx_write(chip, LP8501_REG_PROG_MEM + i, 0); + + i = 0; + while ((offset < size - 1) && (i < LP8501_PROGRAM_LENGTH)) { + /* separate sscanfs because length is working only for %s */ + ret = sscanf(data + offset, "%2s%n ", c, &nrchars); + if (ret != 1) + goto err; + + ret = sscanf(c, "%2x", &cmd); + if (ret != 1) + goto err; + + pattern[i] = (u8)cmd; + offset += nrchars; + i++; + } + + /* Each instruction is 16bit long. Check that length is even */ + if (i % 2) + goto err; + + update_size = i; + for (i = 0; i < update_size; i++) + lp55xx_write(chip, LP8501_REG_PROG_MEM + i, pattern[i]); + + return 0; + +err: + dev_err(&chip->cl->dev, "wrong pattern format\n"); + return -EINVAL; +} + +static void lp8501_firmware_loaded(struct lp55xx_chip *chip) +{ + const struct firmware *fw = chip->fw; + + if (fw->size > LP8501_PROGRAM_LENGTH) { + dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", + fw->size); + return; + } + + /* + * Program momery sequence + * 1) set engine mode to "LOAD" + * 2) write firmware data into program memory + */ + + lp8501_load_engine(chip); + lp8501_update_program_memory(chip, fw->data, fw->size); +} + +static void lp8501_led_brightness_work(struct work_struct *work) +{ + struct lp55xx_led *led = container_of(work, struct lp55xx_led, + brightness_work); + struct lp55xx_chip *chip = led->chip; + + mutex_lock(&chip->lock); + lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr, + led->brightness); + mutex_unlock(&chip->lock); +} + +/* Chip specific configurations */ +static struct lp55xx_device_config lp8501_cfg = { + .reset = { + .addr = LP8501_REG_RESET, + .val = LP8501_RESET, + }, + .enable = { + .addr = LP8501_REG_ENABLE, + .val = LP8501_ENABLE, + }, + .max_channel = LP8501_MAX_LEDS, + .post_init_device = lp8501_post_init_device, + .brightness_work_fn = lp8501_led_brightness_work, + .set_led_current = lp8501_set_led_current, + .firmware_cb = lp8501_firmware_loaded, + .run_engine = lp8501_run_engine, +}; + +static int lp8501_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct lp55xx_chip *chip; + struct lp55xx_led *led; + struct lp55xx_platform_data *pdata; + struct device_node *np = client->dev.of_node; + + if (!client->dev.platform_data) { + if (np) { + ret = lp55xx_of_populate_pdata(&client->dev, np); + if (ret < 0) + return ret; + } else { + dev_err(&client->dev, "no platform data\n"); + return -EINVAL; + } + } + pdata = client->dev.platform_data; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + led = devm_kzalloc(&client->dev, + sizeof(*led) * pdata->num_channels, GFP_KERNEL); + if (!led) + return -ENOMEM; + + chip->cl = client; + chip->pdata = pdata; + chip->cfg = &lp8501_cfg; + + mutex_init(&chip->lock); + + i2c_set_clientdata(client, led); + + ret = lp55xx_init_device(chip); + if (ret) + goto err_init; + + dev_info(&client->dev, "%s Programmable led chip found\n", id->name); + + ret = lp55xx_register_leds(led, chip); + if (ret) + goto err_register_leds; + + ret = lp55xx_register_sysfs(chip); + if (ret) { + dev_err(&client->dev, "registering sysfs failed\n"); + goto err_register_sysfs; + } + + return 0; + +err_register_sysfs: + lp55xx_unregister_leds(led, chip); +err_register_leds: + lp55xx_deinit_device(chip); +err_init: + return ret; +} + +static int lp8501_remove(struct i2c_client *client) +{ + struct lp55xx_led *led = i2c_get_clientdata(client); + struct lp55xx_chip *chip = led->chip; + + lp8501_stop_engine(chip); + lp55xx_unregister_sysfs(chip); + lp55xx_unregister_leds(led, chip); + lp55xx_deinit_device(chip); + + return 0; +} + +static const struct i2c_device_id lp8501_id[] = { + { "lp8501", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lp8501_id); + +#ifdef CONFIG_OF +static const struct of_device_id of_lp8501_leds_match[] = { + { .compatible = "ti,lp8501", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, of_lp8501_leds_match); +#endif + +static struct i2c_driver lp8501_driver = { + .driver = { + .name = "lp8501", + .of_match_table = of_match_ptr(of_lp8501_leds_match), + }, + .probe = lp8501_probe, + .remove = lp8501_remove, + .id_table = lp8501_id, +}; + +module_i2c_driver(lp8501_driver); + +MODULE_DESCRIPTION("Texas Instruments LP8501 LED drvier"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/leds-lp55xx.h b/include/linux/platform_data/leds-lp55xx.h index 202e290faea8..51a2ff579d60 100644 --- a/include/linux/platform_data/leds-lp55xx.h +++ b/include/linux/platform_data/leds-lp55xx.h @@ -36,6 +36,13 @@ struct lp55xx_predef_pattern { u8 size_b; }; +enum lp8501_pwr_sel { + LP8501_ALL_VDD, /* D1~9 are connected to VDD */ + LP8501_6VDD_3VOUT, /* D1~6 with VDD, D7~9 with VOUT */ + LP8501_3VDD_6VOUT, /* D1~6 with VOUT, D7~9 with VDD */ + LP8501_ALL_VOUT, /* D1~9 are connected to VOUT */ +}; + /* * struct lp55xx_platform_data * @led_config : Configurable led class device @@ -67,6 +74,9 @@ struct lp55xx_platform_data { /* Predefined pattern data */ struct lp55xx_predef_pattern *patterns; unsigned int num_patterns; + + /* LP8501 specific */ + enum lp8501_pwr_sel pwr_sel; }; #endif /* _LEDS_LP55XX_H */ -- cgit v1.2.3-71-gd317 From 8465b01827b7a1e0e2464b0a300618bf7add25d8 Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Thu, 25 Jul 2013 10:16:41 -0700 Subject: leds: pca9633: Add hardware blink support Add hardware blinking support to the pca9633 driver. NOTE: Hardware blinking violates the leds infrastructure driver interface since the hardware only supports blinking all LEDs with the same delay_on/delay_off rates. That is, only the LEDs that are set to blink will actually blink but all LEDs that are set to blink will blink in identical fashion. The delay_on/delay_off values of the last LED that is set to blink will be used for all of the blinking LEDs. If the hardware doesn't support the requested blinking pattern, a default of 500ms on and off will be used. Hardware blinking is disabled by default but can be enabled by setting the 'blink_type' member in the platform_data struct to 'PCA9633_HW_BLINK' or by adding the 'nxp,hw-blink' property to the DTS. (fengguang.wu@intel.com: Removes unneeded semicolon.) Signed-off-by: Mark A. Greer Reported-by: Fengguang Wu Signed-off-by: Bryan Wu --- Documentation/devicetree/bindings/leds/pca9633.txt | 1 + drivers/leds/leds-pca9633.c | 136 ++++++++++++++++++++- include/linux/platform_data/leds-pca9633.h | 6 + 3 files changed, 139 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/leds/pca9633.txt b/Documentation/devicetree/bindings/leds/pca9633.txt index 8140512c0ac8..6d9e1a9e7e9f 100644 --- a/Documentation/devicetree/bindings/leds/pca9633.txt +++ b/Documentation/devicetree/bindings/leds/pca9633.txt @@ -5,6 +5,7 @@ Required properties: Optional properties: - nxp,totem-pole : use totem pole (push-pull) instead of default open-drain +- nxp,hw-blink : use hardware blinking instead of software blinking Each led is represented as a sub-node of the nxp,pca9633 device. diff --git a/drivers/leds/leds-pca9633.c b/drivers/leds/leds-pca9633.c index 90935e465c4d..77b3f24b7207 100644 --- a/drivers/leds/leds-pca9633.c +++ b/drivers/leds/leds-pca9633.c @@ -11,6 +11,15 @@ * * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62) * + * Note that hardware blinking violates the leds infrastructure driver + * interface since the hardware only supports blinking all LEDs with the + * same delay_on/delay_off rates. That is, only the LEDs that are set to + * blink will actually blink but all LEDs that are set to blink will blink + * in identical fashion. The delay_on/delay_off values of the last LED + * that is set to blink will be used for all of the blinking LEDs. + * Hardware blinking is disabled by default but can be enabled by setting + * the 'blink_type' member in the platform_data struct to 'PCA9633_HW_BLINK' + * or by adding the 'nxp,hw-blink' property to the DTS. */ #include @@ -31,30 +40,44 @@ #define PCA9633_LED_PWM 0x2 /* Controlled through PWM */ #define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ +#define PCA9633_MODE2_DMBLNK 0x20 /* Enable blinking */ + #define PCA9633_MODE1 0x00 #define PCA9633_MODE2 0x01 #define PCA9633_PWM_BASE 0x02 +#define PCA9633_GRPPWM 0x06 +#define PCA9633_GRPFREQ 0x07 #define PCA9633_LEDOUT 0x08 +/* Total blink period in milliseconds */ +#define PCA9632_BLINK_PERIOD_MIN 42 +#define PCA9632_BLINK_PERIOD_MAX 10667 + static const struct i2c_device_id pca9633_id[] = { { "pca9633", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, pca9633_id); +enum pca9633_cmd { + BRIGHTNESS_SET, + BLINK_SET, +}; + struct pca9633_led { struct i2c_client *client; struct work_struct work; enum led_brightness brightness; struct led_classdev led_cdev; int led_num; /* 0 .. 3 potentially */ + enum pca9633_cmd cmd; char name[32]; + u8 gdc; + u8 gfrq; }; -static void pca9633_led_work(struct work_struct *work) +static void pca9633_brightness_work(struct pca9633_led *pca9633) { - struct pca9633_led *pca9633 = container_of(work, - struct pca9633_led, work); u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT); int shift = 2 * pca9633->led_num; u8 mask = 0x3 << shift; @@ -78,6 +101,43 @@ static void pca9633_led_work(struct work_struct *work) } } +static void pca9633_blink_work(struct pca9633_led *pca9633) +{ + u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT); + u8 mode2 = i2c_smbus_read_byte_data(pca9633->client, PCA9633_MODE2); + int shift = 2 * pca9633->led_num; + u8 mask = 0x3 << shift; + + i2c_smbus_write_byte_data(pca9633->client, PCA9633_GRPPWM, + pca9633->gdc); + + i2c_smbus_write_byte_data(pca9633->client, PCA9633_GRPFREQ, + pca9633->gfrq); + + if (!(mode2 & PCA9633_MODE2_DMBLNK)) + i2c_smbus_write_byte_data(pca9633->client, PCA9633_MODE2, + mode2 | PCA9633_MODE2_DMBLNK); + + if ((ledout & mask) != (PCA9633_LED_GRP_PWM << shift)) + i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, + (ledout & ~mask) | (PCA9633_LED_GRP_PWM << shift)); +} + +static void pca9633_work(struct work_struct *work) +{ + struct pca9633_led *pca9633 = container_of(work, + struct pca9633_led, work); + + switch (pca9633->cmd) { + case BRIGHTNESS_SET: + pca9633_brightness_work(pca9633); + break; + case BLINK_SET: + pca9633_blink_work(pca9633); + break; + } +} + static void pca9633_led_set(struct led_classdev *led_cdev, enum led_brightness value) { @@ -85,6 +145,7 @@ static void pca9633_led_set(struct led_classdev *led_cdev, pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev); + pca9633->cmd = BRIGHTNESS_SET; pca9633->brightness = value; /* @@ -94,6 +155,64 @@ static void pca9633_led_set(struct led_classdev *led_cdev, schedule_work(&pca9633->work); } +static int pca9633_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, unsigned long *delay_off) +{ + struct pca9633_led *pca9633; + unsigned long time_on, time_off, period; + u8 gdc, gfrq; + + pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev); + + time_on = *delay_on; + time_off = *delay_off; + + /* If both zero, pick reasonable defaults of 500ms each */ + if (!time_on && !time_off) { + time_on = 500; + time_off = 500; + } + + period = time_on + time_off; + + /* If period not supported by hardware, default to someting sane. */ + if ((period < PCA9632_BLINK_PERIOD_MIN) || + (period > PCA9632_BLINK_PERIOD_MAX)) { + time_on = 500; + time_off = 500; + period = time_on + time_off; + } + + /* + * From manual: duty cycle = (GDC / 256) -> + * (time_on / period) = (GDC / 256) -> + * GDC = ((time_on * 256) / period) + */ + gdc = (time_on * 256) / period; + + /* + * From manual: period = ((GFRQ + 1) / 24) in seconds. + * So, period (in ms) = (((GFRQ + 1) / 24) * 1000) -> + * GFRQ = ((period * 24 / 1000) - 1) + */ + gfrq = (period * 24 / 1000) - 1; + + pca9633->cmd = BLINK_SET; + pca9633->gdc = gdc; + pca9633->gfrq = gfrq; + + /* + * Must use workqueue for the actual I/O since I2C operations + * can sleep. + */ + schedule_work(&pca9633->work); + + *delay_on = time_on; + *delay_off = time_off; + + return 0; +} + #if IS_ENABLED(CONFIG_OF) static struct pca9633_platform_data * pca9633_dt_init(struct i2c_client *client) @@ -140,6 +259,12 @@ pca9633_dt_init(struct i2c_client *client) else pdata->outdrv = PCA9633_OPEN_DRAIN; + /* default to software blinking unless hardware blinking is specified */ + if (of_property_read_bool(np, "nxp,hw-blink")) + pdata->blink_type = PCA9633_HW_BLINK; + else + pdata->blink_type = PCA9633_SW_BLINK; + return pdata; } @@ -206,7 +331,10 @@ static int pca9633_probe(struct i2c_client *client, pca9633[i].led_cdev.name = pca9633[i].name; pca9633[i].led_cdev.brightness_set = pca9633_led_set; - INIT_WORK(&pca9633[i].work, pca9633_led_work); + if (pdata && pdata->blink_type == PCA9633_HW_BLINK) + pca9633[i].led_cdev.blink_set = pca9633_blink_set; + + INIT_WORK(&pca9633[i].work, pca9633_work); err = led_classdev_register(&client->dev, &pca9633[i].led_cdev); if (err < 0) diff --git a/include/linux/platform_data/leds-pca9633.h b/include/linux/platform_data/leds-pca9633.h index c5bf29b6fa7f..3c1037a81d34 100644 --- a/include/linux/platform_data/leds-pca9633.h +++ b/include/linux/platform_data/leds-pca9633.h @@ -27,9 +27,15 @@ enum pca9633_outdrv { PCA9633_TOTEM_POLE, /* aka push-pull */ }; +enum pca9633_blink_type { + PCA9633_SW_BLINK, + PCA9633_HW_BLINK, +}; + struct pca9633_platform_data { struct led_platform_data leds; enum pca9633_outdrv outdrv; + enum pca9633_blink_type blink_type; }; #endif /* __LINUX_PCA9633_H*/ -- cgit v1.2.3-71-gd317 From 56a1740c21e4396164265c3ec80e29990ddcdc36 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Delgado Date: Wed, 14 Aug 2013 14:23:50 -0700 Subject: leds-pca9633: Rename to leds-pca963x The driver now supports the chips pca9633 and pca9634, therefore we rename the files to more generic and meaningul names Signed-off-by: Ricardo Ribalda Delgado Signed-off-by: Bryan Wu --- Documentation/devicetree/bindings/leds/pca9633.txt | 47 --- Documentation/devicetree/bindings/leds/pca963x.txt | 47 +++ drivers/leds/Kconfig | 2 +- drivers/leds/Makefile | 2 +- drivers/leds/leds-pca9633.c | 461 --------------------- drivers/leds/leds-pca963x.c | 461 +++++++++++++++++++++ include/linux/platform_data/leds-pca9633.h | 41 -- include/linux/platform_data/leds-pca963x.h | 42 ++ 8 files changed, 552 insertions(+), 551 deletions(-) delete mode 100644 Documentation/devicetree/bindings/leds/pca9633.txt create mode 100644 Documentation/devicetree/bindings/leds/pca963x.txt delete mode 100644 drivers/leds/leds-pca9633.c create mode 100644 drivers/leds/leds-pca963x.c delete mode 100644 include/linux/platform_data/leds-pca9633.h create mode 100644 include/linux/platform_data/leds-pca963x.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/leds/pca9633.txt b/Documentation/devicetree/bindings/leds/pca9633.txt deleted file mode 100644 index aece3eac1b63..000000000000 --- a/Documentation/devicetree/bindings/leds/pca9633.txt +++ /dev/null @@ -1,47 +0,0 @@ -LEDs connected to pca9632, pca9633 or pca9634 - -Required properties: -- compatible : should be : "nxp,pca9632", "nxp,pca9633" or "nxp,pca9634" - -Optional properties: -- nxp,totem-pole : use totem pole (push-pull) instead of default open-drain -- nxp,hw-blink : use hardware blinking instead of software blinking - -Each led is represented as a sub-node of the nxp,pca963x device. - -LED sub-node properties: -- label : (optional) see Documentation/devicetree/bindings/leds/common.txt -- reg : number of LED line (could be from 0 to 3 in pca9632 or pca9633 - or 0 to 7 in pca9634) -- linux,default-trigger : (optional) - see Documentation/devicetree/bindings/leds/common.txt - -Examples: - -pca9632: pca9632 { - compatible = "nxp,pca9632"; - #address-cells = <1>; - #size-cells = <0>; - reg = <0x62>; - - red@0 { - label = "red"; - reg = <0>; - linux,default-trigger = "none"; - }; - green@1 { - label = "green"; - reg = <1>; - linux,default-trigger = "none"; - }; - blue@2 { - label = "blue"; - reg = <2>; - linux,default-trigger = "none"; - }; - unused@3 { - label = "unused"; - reg = <3>; - linux,default-trigger = "none"; - }; -}; diff --git a/Documentation/devicetree/bindings/leds/pca963x.txt b/Documentation/devicetree/bindings/leds/pca963x.txt new file mode 100644 index 000000000000..aece3eac1b63 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/pca963x.txt @@ -0,0 +1,47 @@ +LEDs connected to pca9632, pca9633 or pca9634 + +Required properties: +- compatible : should be : "nxp,pca9632", "nxp,pca9633" or "nxp,pca9634" + +Optional properties: +- nxp,totem-pole : use totem pole (push-pull) instead of default open-drain +- nxp,hw-blink : use hardware blinking instead of software blinking + +Each led is represented as a sub-node of the nxp,pca963x device. + +LED sub-node properties: +- label : (optional) see Documentation/devicetree/bindings/leds/common.txt +- reg : number of LED line (could be from 0 to 3 in pca9632 or pca9633 + or 0 to 7 in pca9634) +- linux,default-trigger : (optional) + see Documentation/devicetree/bindings/leds/common.txt + +Examples: + +pca9632: pca9632 { + compatible = "nxp,pca9632"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x62>; + + red@0 { + label = "red"; + reg = <0>; + linux,default-trigger = "none"; + }; + green@1 { + label = "green"; + reg = <1>; + linux,default-trigger = "none"; + }; + blue@2 { + label = "blue"; + reg = <2>; + linux,default-trigger = "none"; + }; + unused@3 { + label = "unused"; + reg = <3>; + linux,default-trigger = "none"; + }; +}; diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index e7977aa3ee59..a1a52bec03d4 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -291,7 +291,7 @@ config LEDS_PCA955X LED driver chips accessed via the I2C bus. Supported devices include PCA9550, PCA9551, PCA9552, and PCA9553. -config LEDS_PCA9633 +config LEDS_PCA963X tristate "LED support for PCA963x I2C chip" depends on LEDS_CLASS depends on I2C diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 3013113e74d2..c7e35423fed9 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -35,7 +35,7 @@ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_FSG) += leds-fsg.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o -obj-$(CONFIG_LEDS_PCA9633) += leds-pca9633.o +obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o obj-$(CONFIG_LEDS_DA9052) += leds-da9052.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o diff --git a/drivers/leds/leds-pca9633.c b/drivers/leds/leds-pca9633.c deleted file mode 100644 index aaa1c4a0dbc5..000000000000 --- a/drivers/leds/leds-pca9633.c +++ /dev/null @@ -1,461 +0,0 @@ -/* - * Copyright 2011 bct electronic GmbH - * Copyright 2013 Qtechnology/AS - * - * Author: Peter Meerwald - * Author: Ricardo Ribalda - * - * Based on leds-pca955x.c - * - * This file is subject to the terms and conditions of version 2 of - * the GNU General Public License. See the file COPYING in the main - * directory of this archive for more details. - * - * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62) - * LED driver for the PCA9634 I2C LED driver (7-bit slave address set by hw.) - * - * Note that hardware blinking violates the leds infrastructure driver - * interface since the hardware only supports blinking all LEDs with the - * same delay_on/delay_off rates. That is, only the LEDs that are set to - * blink will actually blink but all LEDs that are set to blink will blink - * in identical fashion. The delay_on/delay_off values of the last LED - * that is set to blink will be used for all of the blinking LEDs. - * Hardware blinking is disabled by default but can be enabled by setting - * the 'blink_type' member in the platform_data struct to 'PCA9633_HW_BLINK' - * or by adding the 'nxp,hw-blink' property to the DTS. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* LED select registers determine the source that drives LED outputs */ -#define PCA9633_LED_OFF 0x0 /* LED driver off */ -#define PCA9633_LED_ON 0x1 /* LED driver on */ -#define PCA9633_LED_PWM 0x2 /* Controlled through PWM */ -#define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ - -#define PCA9633_MODE2_DMBLNK 0x20 /* Enable blinking */ - -#define PCA9633_MODE1 0x00 -#define PCA9633_MODE2 0x01 -#define PCA9633_PWM_BASE 0x02 - -enum pca9633_type { - pca9633, - pca9634, -}; - -struct pca9633_chipdef { - u8 grppwm; - u8 grpfreq; - u8 ledout_base; - int n_leds; -}; - -static struct pca9633_chipdef pca9633_chipdefs[] = { - [pca9633] = { - .grppwm = 0x6, - .grpfreq = 0x7, - .ledout_base = 0x8, - .n_leds = 4, - }, - [pca9634] = { - .grppwm = 0xa, - .grpfreq = 0xb, - .ledout_base = 0xc, - .n_leds = 8, - }, -}; - -/* Total blink period in milliseconds */ -#define PCA9632_BLINK_PERIOD_MIN 42 -#define PCA9632_BLINK_PERIOD_MAX 10667 - -static const struct i2c_device_id pca9633_id[] = { - { "pca9632", pca9633 }, - { "pca9633", pca9633 }, - { "pca9634", pca9634 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, pca9633_id); - -enum pca9633_cmd { - BRIGHTNESS_SET, - BLINK_SET, -}; - -struct pca9633_led; - -struct pca9633 { - struct pca9633_chipdef *chipdef; - struct mutex mutex; - struct i2c_client *client; - struct pca9633_led *leds; -}; - -struct pca9633_led { - struct pca9633 *chip; - struct work_struct work; - enum led_brightness brightness; - struct led_classdev led_cdev; - int led_num; /* 0 .. 7 potentially */ - enum pca9633_cmd cmd; - char name[32]; - u8 gdc; - u8 gfrq; -}; - -static void pca9633_brightness_work(struct pca9633_led *pca9633) -{ - u8 ledout_addr = pca9633->chip->chipdef->ledout_base - + (pca9633->led_num / 4); - u8 ledout; - int shift = 2 * (pca9633->led_num % 4); - u8 mask = 0x3 << shift; - - mutex_lock(&pca9633->chip->mutex); - ledout = i2c_smbus_read_byte_data(pca9633->chip->client, ledout_addr); - switch (pca9633->brightness) { - case LED_FULL: - i2c_smbus_write_byte_data(pca9633->chip->client, ledout_addr, - (ledout & ~mask) | (PCA9633_LED_ON << shift)); - break; - case LED_OFF: - i2c_smbus_write_byte_data(pca9633->chip->client, ledout_addr, - ledout & ~mask); - break; - default: - i2c_smbus_write_byte_data(pca9633->chip->client, - PCA9633_PWM_BASE + pca9633->led_num, - pca9633->brightness); - i2c_smbus_write_byte_data(pca9633->chip->client, ledout_addr, - (ledout & ~mask) | (PCA9633_LED_PWM << shift)); - break; - } - mutex_unlock(&pca9633->chip->mutex); -} - -static void pca9633_blink_work(struct pca9633_led *pca9633) -{ - u8 ledout_addr = pca9633->chip->chipdef->ledout_base + - (pca9633->led_num / 4); - u8 ledout; - u8 mode2 = i2c_smbus_read_byte_data(pca9633->chip->client, - PCA9633_MODE2); - int shift = 2 * (pca9633->led_num % 4); - u8 mask = 0x3 << shift; - - i2c_smbus_write_byte_data(pca9633->chip->client, - pca9633->chip->chipdef->grppwm, pca9633->gdc); - - i2c_smbus_write_byte_data(pca9633->chip->client, - pca9633->chip->chipdef->grpfreq, pca9633->gfrq); - - if (!(mode2 & PCA9633_MODE2_DMBLNK)) - i2c_smbus_write_byte_data(pca9633->chip->client, PCA9633_MODE2, - mode2 | PCA9633_MODE2_DMBLNK); - - mutex_lock(&pca9633->chip->mutex); - ledout = i2c_smbus_read_byte_data(pca9633->chip->client, ledout_addr); - if ((ledout & mask) != (PCA9633_LED_GRP_PWM << shift)) - i2c_smbus_write_byte_data(pca9633->chip->client, ledout_addr, - (ledout & ~mask) | (PCA9633_LED_GRP_PWM << shift)); - mutex_unlock(&pca9633->chip->mutex); -} - -static void pca9633_work(struct work_struct *work) -{ - struct pca9633_led *pca9633 = container_of(work, - struct pca9633_led, work); - - switch (pca9633->cmd) { - case BRIGHTNESS_SET: - pca9633_brightness_work(pca9633); - break; - case BLINK_SET: - pca9633_blink_work(pca9633); - break; - } -} - -static void pca9633_led_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - struct pca9633_led *pca9633; - - pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev); - - pca9633->cmd = BRIGHTNESS_SET; - pca9633->brightness = value; - - /* - * Must use workqueue for the actual I/O since I2C operations - * can sleep. - */ - schedule_work(&pca9633->work); -} - -static int pca9633_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, unsigned long *delay_off) -{ - struct pca9633_led *pca9633; - unsigned long time_on, time_off, period; - u8 gdc, gfrq; - - pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev); - - time_on = *delay_on; - time_off = *delay_off; - - /* If both zero, pick reasonable defaults of 500ms each */ - if (!time_on && !time_off) { - time_on = 500; - time_off = 500; - } - - period = time_on + time_off; - - /* If period not supported by hardware, default to someting sane. */ - if ((period < PCA9632_BLINK_PERIOD_MIN) || - (period > PCA9632_BLINK_PERIOD_MAX)) { - time_on = 500; - time_off = 500; - period = time_on + time_off; - } - - /* - * From manual: duty cycle = (GDC / 256) -> - * (time_on / period) = (GDC / 256) -> - * GDC = ((time_on * 256) / period) - */ - gdc = (time_on * 256) / period; - - /* - * From manual: period = ((GFRQ + 1) / 24) in seconds. - * So, period (in ms) = (((GFRQ + 1) / 24) * 1000) -> - * GFRQ = ((period * 24 / 1000) - 1) - */ - gfrq = (period * 24 / 1000) - 1; - - pca9633->cmd = BLINK_SET; - pca9633->gdc = gdc; - pca9633->gfrq = gfrq; - - /* - * Must use workqueue for the actual I/O since I2C operations - * can sleep. - */ - schedule_work(&pca9633->work); - - *delay_on = time_on; - *delay_off = time_off; - - return 0; -} - -#if IS_ENABLED(CONFIG_OF) -static struct pca9633_platform_data * -pca9633_dt_init(struct i2c_client *client, struct pca9633_chipdef *chip) -{ - struct device_node *np = client->dev.of_node, *child; - struct pca9633_platform_data *pdata; - struct led_info *pca9633_leds; - int count; - - count = of_get_child_count(np); - if (!count || count > chip->n_leds) - return ERR_PTR(-ENODEV); - - pca9633_leds = devm_kzalloc(&client->dev, - sizeof(struct led_info) * chip->n_leds, GFP_KERNEL); - if (!pca9633_leds) - return ERR_PTR(-ENOMEM); - - for_each_child_of_node(np, child) { - struct led_info led; - u32 reg; - int res; - - led.name = - of_get_property(child, "label", NULL) ? : child->name; - led.default_trigger = - of_get_property(child, "linux,default-trigger", NULL); - res = of_property_read_u32(child, "reg", ®); - if (res != 0) - continue; - pca9633_leds[reg] = led; - } - pdata = devm_kzalloc(&client->dev, - sizeof(struct pca9633_platform_data), GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - - pdata->leds.leds = pca9633_leds; - pdata->leds.num_leds = count; - - /* default to open-drain unless totem pole (push-pull) is specified */ - if (of_property_read_bool(np, "nxp,totem-pole")) - pdata->outdrv = PCA9633_TOTEM_POLE; - else - pdata->outdrv = PCA9633_OPEN_DRAIN; - - /* default to software blinking unless hardware blinking is specified */ - if (of_property_read_bool(np, "nxp,hw-blink")) - pdata->blink_type = PCA9633_HW_BLINK; - else - pdata->blink_type = PCA9633_SW_BLINK; - - return pdata; -} - -static const struct of_device_id of_pca9633_match[] = { - { .compatible = "nxp,pca9632", }, - { .compatible = "nxp,pca9633", }, - { .compatible = "nxp,pca9634", }, - {}, -}; -#else -static struct pca9633_platform_data * -pca9633_dt_init(struct i2c_client *client, struct pca9633_chipdef *chip) -{ - return ERR_PTR(-ENODEV); -} -#endif - -static int pca9633_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct pca9633 *pca9633_chip; - struct pca9633_led *pca9633; - struct pca9633_platform_data *pdata; - struct pca9633_chipdef *chip; - int i, err; - - chip = &pca9633_chipdefs[id->driver_data]; - pdata = dev_get_platdata(&client->dev); - - if (!pdata) { - pdata = pca9633_dt_init(client, chip); - if (IS_ERR(pdata)) { - dev_warn(&client->dev, "could not parse configuration\n"); - pdata = NULL; - } - } - - if (pdata && (pdata->leds.num_leds < 1 || - pdata->leds.num_leds > chip->n_leds)) { - dev_err(&client->dev, "board info must claim 1-%d LEDs", - chip->n_leds); - return -EINVAL; - } - - pca9633_chip = devm_kzalloc(&client->dev, sizeof(*pca9633_chip), - GFP_KERNEL); - if (!pca9633_chip) - return -ENOMEM; - pca9633 = devm_kzalloc(&client->dev, chip->n_leds * sizeof(*pca9633), - GFP_KERNEL); - if (!pca9633) - return -ENOMEM; - - i2c_set_clientdata(client, pca9633_chip); - - mutex_init(&pca9633_chip->mutex); - pca9633_chip->chipdef = chip; - pca9633_chip->client = client; - pca9633_chip->leds = pca9633; - - /* Turn off LEDs by default*/ - i2c_smbus_write_byte_data(client, chip->ledout_base, 0x00); - if (chip->n_leds > 4) - i2c_smbus_write_byte_data(client, chip->ledout_base + 1, 0x00); - - for (i = 0; i < chip->n_leds; i++) { - pca9633[i].led_num = i; - pca9633[i].chip = pca9633_chip; - - /* Platform data can specify LED names and default triggers */ - if (pdata && i < pdata->leds.num_leds) { - if (pdata->leds.leds[i].name) - snprintf(pca9633[i].name, - sizeof(pca9633[i].name), "pca9633:%s", - pdata->leds.leds[i].name); - if (pdata->leds.leds[i].default_trigger) - pca9633[i].led_cdev.default_trigger = - pdata->leds.leds[i].default_trigger; - } - if (!pdata || i >= pdata->leds.num_leds || - !pdata->leds.leds[i].name) - snprintf(pca9633[i].name, sizeof(pca9633[i].name), - "pca9633:%d:%.2x:%d", client->adapter->nr, - client->addr, i); - - pca9633[i].led_cdev.name = pca9633[i].name; - pca9633[i].led_cdev.brightness_set = pca9633_led_set; - - if (pdata && pdata->blink_type == PCA9633_HW_BLINK) - pca9633[i].led_cdev.blink_set = pca9633_blink_set; - - INIT_WORK(&pca9633[i].work, pca9633_work); - - err = led_classdev_register(&client->dev, &pca9633[i].led_cdev); - if (err < 0) - goto exit; - } - - /* Disable LED all-call address and set normal mode */ - i2c_smbus_write_byte_data(client, PCA9633_MODE1, 0x00); - - /* Configure output: open-drain or totem pole (push-pull) */ - if (pdata && pdata->outdrv == PCA9633_OPEN_DRAIN) - i2c_smbus_write_byte_data(client, PCA9633_MODE2, 0x01); - - return 0; - -exit: - while (i--) { - led_classdev_unregister(&pca9633[i].led_cdev); - cancel_work_sync(&pca9633[i].work); - } - - return err; -} - -static int pca9633_remove(struct i2c_client *client) -{ - struct pca9633 *pca9633 = i2c_get_clientdata(client); - int i; - - for (i = 0; i < pca9633->chipdef->n_leds; i++) { - led_classdev_unregister(&pca9633->leds[i].led_cdev); - cancel_work_sync(&pca9633->leds[i].work); - } - - return 0; -} - -static struct i2c_driver pca9633_driver = { - .driver = { - .name = "leds-pca9633", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(of_pca9633_match), - }, - .probe = pca9633_probe, - .remove = pca9633_remove, - .id_table = pca9633_id, -}; - -module_i2c_driver(pca9633_driver); - -MODULE_AUTHOR("Peter Meerwald "); -MODULE_DESCRIPTION("PCA9633 LED driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c new file mode 100644 index 000000000000..35d56a62f92f --- /dev/null +++ b/drivers/leds/leds-pca963x.c @@ -0,0 +1,461 @@ +/* + * Copyright 2011 bct electronic GmbH + * Copyright 2013 Qtechnology/AS + * + * Author: Peter Meerwald + * Author: Ricardo Ribalda + * + * Based on leds-pca955x.c + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62) + * LED driver for the PCA9634 I2C LED driver (7-bit slave address set by hw.) + * + * Note that hardware blinking violates the leds infrastructure driver + * interface since the hardware only supports blinking all LEDs with the + * same delay_on/delay_off rates. That is, only the LEDs that are set to + * blink will actually blink but all LEDs that are set to blink will blink + * in identical fashion. The delay_on/delay_off values of the last LED + * that is set to blink will be used for all of the blinking LEDs. + * Hardware blinking is disabled by default but can be enabled by setting + * the 'blink_type' member in the platform_data struct to 'PCA963X_HW_BLINK' + * or by adding the 'nxp,hw-blink' property to the DTS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* LED select registers determine the source that drives LED outputs */ +#define PCA963X_LED_OFF 0x0 /* LED driver off */ +#define PCA963X_LED_ON 0x1 /* LED driver on */ +#define PCA963X_LED_PWM 0x2 /* Controlled through PWM */ +#define PCA963X_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ + +#define PCA963X_MODE2_DMBLNK 0x20 /* Enable blinking */ + +#define PCA963X_MODE1 0x00 +#define PCA963X_MODE2 0x01 +#define PCA963X_PWM_BASE 0x02 + +enum pca963x_type { + pca9633, + pca9634, +}; + +struct pca963x_chipdef { + u8 grppwm; + u8 grpfreq; + u8 ledout_base; + int n_leds; +}; + +static struct pca963x_chipdef pca963x_chipdefs[] = { + [pca9633] = { + .grppwm = 0x6, + .grpfreq = 0x7, + .ledout_base = 0x8, + .n_leds = 4, + }, + [pca9634] = { + .grppwm = 0xa, + .grpfreq = 0xb, + .ledout_base = 0xc, + .n_leds = 8, + }, +}; + +/* Total blink period in milliseconds */ +#define PCA963X_BLINK_PERIOD_MIN 42 +#define PCA963X_BLINK_PERIOD_MAX 10667 + +static const struct i2c_device_id pca963x_id[] = { + { "pca9632", pca9633 }, + { "pca9633", pca9633 }, + { "pca9634", pca9634 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca963x_id); + +enum pca963x_cmd { + BRIGHTNESS_SET, + BLINK_SET, +}; + +struct pca963x_led; + +struct pca963x { + struct pca963x_chipdef *chipdef; + struct mutex mutex; + struct i2c_client *client; + struct pca963x_led *leds; +}; + +struct pca963x_led { + struct pca963x *chip; + struct work_struct work; + enum led_brightness brightness; + struct led_classdev led_cdev; + int led_num; /* 0 .. 7 potentially */ + enum pca963x_cmd cmd; + char name[32]; + u8 gdc; + u8 gfrq; +}; + +static void pca963x_brightness_work(struct pca963x_led *pca963x) +{ + u8 ledout_addr = pca963x->chip->chipdef->ledout_base + + (pca963x->led_num / 4); + u8 ledout; + int shift = 2 * (pca963x->led_num % 4); + u8 mask = 0x3 << shift; + + mutex_lock(&pca963x->chip->mutex); + ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr); + switch (pca963x->brightness) { + case LED_FULL: + i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr, + (ledout & ~mask) | (PCA963X_LED_ON << shift)); + break; + case LED_OFF: + i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr, + ledout & ~mask); + break; + default: + i2c_smbus_write_byte_data(pca963x->chip->client, + PCA963X_PWM_BASE + pca963x->led_num, + pca963x->brightness); + i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr, + (ledout & ~mask) | (PCA963X_LED_PWM << shift)); + break; + } + mutex_unlock(&pca963x->chip->mutex); +} + +static void pca963x_blink_work(struct pca963x_led *pca963x) +{ + u8 ledout_addr = pca963x->chip->chipdef->ledout_base + + (pca963x->led_num / 4); + u8 ledout; + u8 mode2 = i2c_smbus_read_byte_data(pca963x->chip->client, + PCA963X_MODE2); + int shift = 2 * (pca963x->led_num % 4); + u8 mask = 0x3 << shift; + + i2c_smbus_write_byte_data(pca963x->chip->client, + pca963x->chip->chipdef->grppwm, pca963x->gdc); + + i2c_smbus_write_byte_data(pca963x->chip->client, + pca963x->chip->chipdef->grpfreq, pca963x->gfrq); + + if (!(mode2 & PCA963X_MODE2_DMBLNK)) + i2c_smbus_write_byte_data(pca963x->chip->client, PCA963X_MODE2, + mode2 | PCA963X_MODE2_DMBLNK); + + mutex_lock(&pca963x->chip->mutex); + ledout = i2c_smbus_read_byte_data(pca963x->chip->client, ledout_addr); + if ((ledout & mask) != (PCA963X_LED_GRP_PWM << shift)) + i2c_smbus_write_byte_data(pca963x->chip->client, ledout_addr, + (ledout & ~mask) | (PCA963X_LED_GRP_PWM << shift)); + mutex_unlock(&pca963x->chip->mutex); +} + +static void pca963x_work(struct work_struct *work) +{ + struct pca963x_led *pca963x = container_of(work, + struct pca963x_led, work); + + switch (pca963x->cmd) { + case BRIGHTNESS_SET: + pca963x_brightness_work(pca963x); + break; + case BLINK_SET: + pca963x_blink_work(pca963x); + break; + } +} + +static void pca963x_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct pca963x_led *pca963x; + + pca963x = container_of(led_cdev, struct pca963x_led, led_cdev); + + pca963x->cmd = BRIGHTNESS_SET; + pca963x->brightness = value; + + /* + * Must use workqueue for the actual I/O since I2C operations + * can sleep. + */ + schedule_work(&pca963x->work); +} + +static int pca963x_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, unsigned long *delay_off) +{ + struct pca963x_led *pca963x; + unsigned long time_on, time_off, period; + u8 gdc, gfrq; + + pca963x = container_of(led_cdev, struct pca963x_led, led_cdev); + + time_on = *delay_on; + time_off = *delay_off; + + /* If both zero, pick reasonable defaults of 500ms each */ + if (!time_on && !time_off) { + time_on = 500; + time_off = 500; + } + + period = time_on + time_off; + + /* If period not supported by hardware, default to someting sane. */ + if ((period < PCA963X_BLINK_PERIOD_MIN) || + (period > PCA963X_BLINK_PERIOD_MAX)) { + time_on = 500; + time_off = 500; + period = time_on + time_off; + } + + /* + * From manual: duty cycle = (GDC / 256) -> + * (time_on / period) = (GDC / 256) -> + * GDC = ((time_on * 256) / period) + */ + gdc = (time_on * 256) / period; + + /* + * From manual: period = ((GFRQ + 1) / 24) in seconds. + * So, period (in ms) = (((GFRQ + 1) / 24) * 1000) -> + * GFRQ = ((period * 24 / 1000) - 1) + */ + gfrq = (period * 24 / 1000) - 1; + + pca963x->cmd = BLINK_SET; + pca963x->gdc = gdc; + pca963x->gfrq = gfrq; + + /* + * Must use workqueue for the actual I/O since I2C operations + * can sleep. + */ + schedule_work(&pca963x->work); + + *delay_on = time_on; + *delay_off = time_off; + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static struct pca963x_platform_data * +pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip) +{ + struct device_node *np = client->dev.of_node, *child; + struct pca963x_platform_data *pdata; + struct led_info *pca963x_leds; + int count; + + count = of_get_child_count(np); + if (!count || count > chip->n_leds) + return ERR_PTR(-ENODEV); + + pca963x_leds = devm_kzalloc(&client->dev, + sizeof(struct led_info) * chip->n_leds, GFP_KERNEL); + if (!pca963x_leds) + return ERR_PTR(-ENOMEM); + + for_each_child_of_node(np, child) { + struct led_info led; + u32 reg; + int res; + + led.name = + of_get_property(child, "label", NULL) ? : child->name; + led.default_trigger = + of_get_property(child, "linux,default-trigger", NULL); + res = of_property_read_u32(child, "reg", ®); + if (res != 0) + continue; + pca963x_leds[reg] = led; + } + pdata = devm_kzalloc(&client->dev, + sizeof(struct pca963x_platform_data), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->leds.leds = pca963x_leds; + pdata->leds.num_leds = count; + + /* default to open-drain unless totem pole (push-pull) is specified */ + if (of_property_read_bool(np, "nxp,totem-pole")) + pdata->outdrv = PCA963X_TOTEM_POLE; + else + pdata->outdrv = PCA963X_OPEN_DRAIN; + + /* default to software blinking unless hardware blinking is specified */ + if (of_property_read_bool(np, "nxp,hw-blink")) + pdata->blink_type = PCA963X_HW_BLINK; + else + pdata->blink_type = PCA963X_SW_BLINK; + + return pdata; +} + +static const struct of_device_id of_pca963x_match[] = { + { .compatible = "nxp,pca9632", }, + { .compatible = "nxp,pca9633", }, + { .compatible = "nxp,pca9634", }, + {}, +}; +#else +static struct pca963x_platform_data * +pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip) +{ + return ERR_PTR(-ENODEV); +} +#endif + +static int pca963x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pca963x *pca963x_chip; + struct pca963x_led *pca963x; + struct pca963x_platform_data *pdata; + struct pca963x_chipdef *chip; + int i, err; + + chip = &pca963x_chipdefs[id->driver_data]; + pdata = dev_get_platdata(&client->dev); + + if (!pdata) { + pdata = pca963x_dt_init(client, chip); + if (IS_ERR(pdata)) { + dev_warn(&client->dev, "could not parse configuration\n"); + pdata = NULL; + } + } + + if (pdata && (pdata->leds.num_leds < 1 || + pdata->leds.num_leds > chip->n_leds)) { + dev_err(&client->dev, "board info must claim 1-%d LEDs", + chip->n_leds); + return -EINVAL; + } + + pca963x_chip = devm_kzalloc(&client->dev, sizeof(*pca963x_chip), + GFP_KERNEL); + if (!pca963x_chip) + return -ENOMEM; + pca963x = devm_kzalloc(&client->dev, chip->n_leds * sizeof(*pca963x), + GFP_KERNEL); + if (!pca963x) + return -ENOMEM; + + i2c_set_clientdata(client, pca963x_chip); + + mutex_init(&pca963x_chip->mutex); + pca963x_chip->chipdef = chip; + pca963x_chip->client = client; + pca963x_chip->leds = pca963x; + + /* Turn off LEDs by default*/ + i2c_smbus_write_byte_data(client, chip->ledout_base, 0x00); + if (chip->n_leds > 4) + i2c_smbus_write_byte_data(client, chip->ledout_base + 1, 0x00); + + for (i = 0; i < chip->n_leds; i++) { + pca963x[i].led_num = i; + pca963x[i].chip = pca963x_chip; + + /* Platform data can specify LED names and default triggers */ + if (pdata && i < pdata->leds.num_leds) { + if (pdata->leds.leds[i].name) + snprintf(pca963x[i].name, + sizeof(pca963x[i].name), "pca963x:%s", + pdata->leds.leds[i].name); + if (pdata->leds.leds[i].default_trigger) + pca963x[i].led_cdev.default_trigger = + pdata->leds.leds[i].default_trigger; + } + if (!pdata || i >= pdata->leds.num_leds || + !pdata->leds.leds[i].name) + snprintf(pca963x[i].name, sizeof(pca963x[i].name), + "pca963x:%d:%.2x:%d", client->adapter->nr, + client->addr, i); + + pca963x[i].led_cdev.name = pca963x[i].name; + pca963x[i].led_cdev.brightness_set = pca963x_led_set; + + if (pdata && pdata->blink_type == PCA963X_HW_BLINK) + pca963x[i].led_cdev.blink_set = pca963x_blink_set; + + INIT_WORK(&pca963x[i].work, pca963x_work); + + err = led_classdev_register(&client->dev, &pca963x[i].led_cdev); + if (err < 0) + goto exit; + } + + /* Disable LED all-call address and set normal mode */ + i2c_smbus_write_byte_data(client, PCA963X_MODE1, 0x00); + + /* Configure output: open-drain or totem pole (push-pull) */ + if (pdata && pdata->outdrv == PCA963X_OPEN_DRAIN) + i2c_smbus_write_byte_data(client, PCA963X_MODE2, 0x01); + + return 0; + +exit: + while (i--) { + led_classdev_unregister(&pca963x[i].led_cdev); + cancel_work_sync(&pca963x[i].work); + } + + return err; +} + +static int pca963x_remove(struct i2c_client *client) +{ + struct pca963x *pca963x = i2c_get_clientdata(client); + int i; + + for (i = 0; i < pca963x->chipdef->n_leds; i++) { + led_classdev_unregister(&pca963x->leds[i].led_cdev); + cancel_work_sync(&pca963x->leds[i].work); + } + + return 0; +} + +static struct i2c_driver pca963x_driver = { + .driver = { + .name = "leds-pca963x", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_pca963x_match), + }, + .probe = pca963x_probe, + .remove = pca963x_remove, + .id_table = pca963x_id, +}; + +module_i2c_driver(pca963x_driver); + +MODULE_AUTHOR("Peter Meerwald "); +MODULE_DESCRIPTION("PCA963X LED driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/platform_data/leds-pca9633.h b/include/linux/platform_data/leds-pca9633.h deleted file mode 100644 index 3c1037a81d34..000000000000 --- a/include/linux/platform_data/leds-pca9633.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * PCA9633 LED chip driver. - * - * Copyright 2012 bct electronic GmbH - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef __LINUX_PCA9633_H -#define __LINUX_PCA9633_H -#include - -enum pca9633_outdrv { - PCA9633_OPEN_DRAIN, - PCA9633_TOTEM_POLE, /* aka push-pull */ -}; - -enum pca9633_blink_type { - PCA9633_SW_BLINK, - PCA9633_HW_BLINK, -}; - -struct pca9633_platform_data { - struct led_platform_data leds; - enum pca9633_outdrv outdrv; - enum pca9633_blink_type blink_type; -}; - -#endif /* __LINUX_PCA9633_H*/ diff --git a/include/linux/platform_data/leds-pca963x.h b/include/linux/platform_data/leds-pca963x.h new file mode 100644 index 000000000000..e731f0036329 --- /dev/null +++ b/include/linux/platform_data/leds-pca963x.h @@ -0,0 +1,42 @@ +/* + * PCA963X LED chip driver. + * + * Copyright 2012 bct electronic GmbH + * Copyright 2013 Qtechnology A/S + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __LINUX_PCA963X_H +#define __LINUX_PCA963X_H +#include + +enum pca963x_outdrv { + PCA963X_OPEN_DRAIN, + PCA963X_TOTEM_POLE, /* aka push-pull */ +}; + +enum pca963x_blink_type { + PCA963X_SW_BLINK, + PCA963X_HW_BLINK, +}; + +struct pca963x_platform_data { + struct led_platform_data leds; + enum pca963x_outdrv outdrv; + enum pca963x_blink_type blink_type; +}; + +#endif /* __LINUX_PCA963X_H*/ -- cgit v1.2.3-71-gd317 From 88d7ebe59341dc3b82e662b80809694e3c6b3766 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 25 Aug 2013 18:28:57 +0200 Subject: drm/vma: add access management helpers The VMA offset manager uses a device-global address-space. Hence, any user can currently map any offset-node they want. They only need to guess the right offset. If we wanted per open-file offset spaces, we'd either need VM_NONLINEAR mappings or multiple "struct address_space" trees. As both doesn't really scale, we implement access management in the VMA manager itself. We use an rb-tree to store open-files for each VMA node. On each mmap call, GEM, TTM or the drivers must check whether the current user is allowed to map this file. We add a separate lock for each node as there is no generic lock available for the caller to protect the node easily. As we currently don't know whether an object may be used for mmap(), we have to do access management for all objects. If it turns out to slow down handle creation/deletion significantly, we can optimize it in several ways: - Most times only a single filp is added per bo so we could use a static "struct file *main_filp" which is checked/added/removed first before we fall back to the rbtree+drm_vma_offset_file. This could be even done lockless with rcu. - Let user-space pass a hint whether mmap() should be supported on the bo and avoid access-management if not. - .. there are probably more ideas once we have benchmarks .. v2: add drm_vma_node_verify_access() helper Signed-off-by: David Herrmann Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_gem.c | 1 + drivers/gpu/drm/drm_vma_manager.c | 155 ++++++++++++++++++++++++++++++++++++++ include/drm/drm_vma_manager.h | 39 +++++++++- 3 files changed, 192 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 1ce88c3301a1..d6122ae6bf86 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -156,6 +156,7 @@ void drm_gem_private_object_init(struct drm_device *dev, kref_init(&obj->refcount); obj->handle_count = 0; obj->size = size; + drm_vma_node_reset(&obj->vma_node); } EXPORT_SYMBOL(drm_gem_private_object_init); diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c index 3837481d5607..63b471205072 100644 --- a/drivers/gpu/drm/drm_vma_manager.c +++ b/drivers/gpu/drm/drm_vma_manager.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,13 @@ * must always be page-aligned (as usual). * If you want to get a valid byte-based user-space address for a given offset, * please see drm_vma_node_offset_addr(). + * + * Additionally to offset management, the vma offset manager also handles access + * management. For every open-file context that is allowed to access a given + * node, you must call drm_vma_node_allow(). Otherwise, an mmap() call on this + * open-file with the offset of the node will fail with -EACCES. To revoke + * access again, use drm_vma_node_revoke(). However, the caller is responsible + * for destroying already existing mappings, if required. */ /** @@ -279,3 +287,150 @@ void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr, write_unlock(&mgr->vm_lock); } EXPORT_SYMBOL(drm_vma_offset_remove); + +/** + * drm_vma_node_allow - Add open-file to list of allowed users + * @node: Node to modify + * @filp: Open file to add + * + * Add @filp to the list of allowed open-files for this node. If @filp is + * already on this list, the ref-count is incremented. + * + * The list of allowed-users is preserved across drm_vma_offset_add() and + * drm_vma_offset_remove() calls. You may even call it if the node is currently + * not added to any offset-manager. + * + * You must remove all open-files the same number of times as you added them + * before destroying the node. Otherwise, you will leak memory. + * + * This is locked against concurrent access internally. + * + * RETURNS: + * 0 on success, negative error code on internal failure (out-of-mem) + */ +int drm_vma_node_allow(struct drm_vma_offset_node *node, struct file *filp) +{ + struct rb_node **iter; + struct rb_node *parent = NULL; + struct drm_vma_offset_file *new, *entry; + int ret = 0; + + /* Preallocate entry to avoid atomic allocations below. It is quite + * unlikely that an open-file is added twice to a single node so we + * don't optimize for this case. OOM is checked below only if the entry + * is actually used. */ + new = kmalloc(sizeof(*entry), GFP_KERNEL); + + write_lock(&node->vm_lock); + + iter = &node->vm_files.rb_node; + + while (likely(*iter)) { + parent = *iter; + entry = rb_entry(*iter, struct drm_vma_offset_file, vm_rb); + + if (filp == entry->vm_filp) { + entry->vm_count++; + goto unlock; + } else if (filp > entry->vm_filp) { + iter = &(*iter)->rb_right; + } else { + iter = &(*iter)->rb_left; + } + } + + if (!new) { + ret = -ENOMEM; + goto unlock; + } + + new->vm_filp = filp; + new->vm_count = 1; + rb_link_node(&new->vm_rb, parent, iter); + rb_insert_color(&new->vm_rb, &node->vm_files); + new = NULL; + +unlock: + write_unlock(&node->vm_lock); + kfree(new); + return ret; +} +EXPORT_SYMBOL(drm_vma_node_allow); + +/** + * drm_vma_node_revoke - Remove open-file from list of allowed users + * @node: Node to modify + * @filp: Open file to remove + * + * Decrement the ref-count of @filp in the list of allowed open-files on @node. + * If the ref-count drops to zero, remove @filp from the list. You must call + * this once for every drm_vma_node_allow() on @filp. + * + * This is locked against concurrent access internally. + * + * If @filp is not on the list, nothing is done. + */ +void drm_vma_node_revoke(struct drm_vma_offset_node *node, struct file *filp) +{ + struct drm_vma_offset_file *entry; + struct rb_node *iter; + + write_lock(&node->vm_lock); + + iter = node->vm_files.rb_node; + while (likely(iter)) { + entry = rb_entry(iter, struct drm_vma_offset_file, vm_rb); + if (filp == entry->vm_filp) { + if (!--entry->vm_count) { + rb_erase(&entry->vm_rb, &node->vm_files); + kfree(entry); + } + break; + } else if (filp > entry->vm_filp) { + iter = iter->rb_right; + } else { + iter = iter->rb_left; + } + } + + write_unlock(&node->vm_lock); +} +EXPORT_SYMBOL(drm_vma_node_revoke); + +/** + * drm_vma_node_is_allowed - Check whether an open-file is granted access + * @node: Node to check + * @filp: Open-file to check for + * + * Search the list in @node whether @filp is currently on the list of allowed + * open-files (see drm_vma_node_allow()). + * + * This is locked against concurrent access internally. + * + * RETURNS: + * true iff @filp is on the list + */ +bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node, + struct file *filp) +{ + struct drm_vma_offset_file *entry; + struct rb_node *iter; + + read_lock(&node->vm_lock); + + iter = node->vm_files.rb_node; + while (likely(iter)) { + entry = rb_entry(iter, struct drm_vma_offset_file, vm_rb); + if (filp == entry->vm_filp) + break; + else if (filp > entry->vm_filp) + iter = iter->rb_right; + else + iter = iter->rb_left; + } + + read_unlock(&node->vm_lock); + + return iter; +} +EXPORT_SYMBOL(drm_vma_node_is_allowed); diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h index 22eedac046ac..c18a593d1744 100644 --- a/include/drm/drm_vma_manager.h +++ b/include/drm/drm_vma_manager.h @@ -24,15 +24,24 @@ */ #include +#include #include #include #include #include #include +struct drm_vma_offset_file { + struct rb_node vm_rb; + struct file *vm_filp; + unsigned long vm_count; +}; + struct drm_vma_offset_node { + rwlock_t vm_lock; struct drm_mm_node vm_node; struct rb_node vm_rb; + struct rb_root vm_files; }; struct drm_vma_offset_manager { @@ -56,6 +65,11 @@ int drm_vma_offset_add(struct drm_vma_offset_manager *mgr, void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr, struct drm_vma_offset_node *node); +int drm_vma_node_allow(struct drm_vma_offset_node *node, struct file *filp); +void drm_vma_node_revoke(struct drm_vma_offset_node *node, struct file *filp); +bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node, + struct file *filp); + /** * drm_vma_offset_exact_lookup() - Look up node by exact address * @mgr: Manager object @@ -122,9 +136,8 @@ static inline void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *m * drm_vma_node_reset() - Initialize or reset node object * @node: Node to initialize or reset * - * Reset a node to its initial state. This must be called if @node isn't - * already cleared (eg., via kzalloc) before using it with any VMA offset - * manager. + * Reset a node to its initial state. This must be called before using it with + * any VMA offset manager. * * This must not be called on an already allocated node, or you will leak * memory. @@ -132,6 +145,8 @@ static inline void drm_vma_offset_unlock_lookup(struct drm_vma_offset_manager *m static inline void drm_vma_node_reset(struct drm_vma_offset_node *node) { memset(node, 0, sizeof(*node)); + node->vm_files = RB_ROOT; + rwlock_init(&node->vm_lock); } /** @@ -221,4 +236,22 @@ static inline void drm_vma_node_unmap(struct drm_vma_offset_node *node, drm_vma_node_size(node) << PAGE_SHIFT, 1); } +/** + * drm_vma_node_verify_access() - Access verification helper for TTM + * @node: Offset node + * @filp: Open-file + * + * This checks whether @filp is granted access to @node. It is the same as + * drm_vma_node_is_allowed() but suitable as drop-in helper for TTM + * verify_access() callbacks. + * + * RETURNS: + * 0 if access is granted, -EACCES otherwise. + */ +static inline int drm_vma_node_verify_access(struct drm_vma_offset_node *node, + struct file *filp) +{ + return drm_vma_node_is_allowed(node, filp) ? 0 : -EACCES; +} + #endif /* __DRM_VMA_MANAGER_H__ */ -- cgit v1.2.3-71-gd317 From e51db73532955dc5eaba4235e62b74b460709d5b Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sat, 30 Mar 2013 19:57:41 -0700 Subject: userns: Better restrictions on when proc and sysfs can be mounted Rely on the fact that another flavor of the filesystem is already mounted and do not rely on state in the user namespace. Verify that the mounted filesystem is not covered in any significant way. I would love to verify that the previously mounted filesystem has no mounts on top but there are at least the directories /proc/sys/fs/binfmt_misc and /sys/fs/cgroup/ that exist explicitly for other filesystems to mount on top of. Refactor the test into a function named fs_fully_visible and call that function from the mount routines of proc and sysfs. This makes this test local to the filesystems involved and the results current of when the mounts take place, removing a weird threading of the user namespace, the mount namespace and the filesystems themselves. Signed-off-by: "Eric W. Biederman" --- fs/namespace.c | 37 +++++++++++++++++++++++++------------ fs/proc/root.c | 7 +++++-- fs/sysfs/mount.c | 3 ++- include/linux/fs.h | 1 + include/linux/user_namespace.h | 4 ---- kernel/user.c | 2 -- kernel/user_namespace.c | 2 -- 7 files changed, 33 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/fs/namespace.c b/fs/namespace.c index 64627f883bf2..877e4277f496 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2867,25 +2867,38 @@ bool current_chrooted(void) return chrooted; } -void update_mnt_policy(struct user_namespace *userns) +bool fs_fully_visible(struct file_system_type *type) { struct mnt_namespace *ns = current->nsproxy->mnt_ns; struct mount *mnt; + bool visible = false; - down_read(&namespace_sem); + if (unlikely(!ns)) + return false; + + namespace_lock(); list_for_each_entry(mnt, &ns->list, mnt_list) { - switch (mnt->mnt.mnt_sb->s_magic) { - case SYSFS_MAGIC: - userns->may_mount_sysfs = true; - break; - case PROC_SUPER_MAGIC: - userns->may_mount_proc = true; - break; + struct mount *child; + if (mnt->mnt.mnt_sb->s_type != type) + continue; + + /* This mount is not fully visible if there are any child mounts + * that cover anything except for empty directories. + */ + list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) { + struct inode *inode = child->mnt_mountpoint->d_inode; + if (!S_ISDIR(inode->i_mode)) + goto next; + if (inode->i_nlink != 2) + goto next; } - if (userns->may_mount_sysfs && userns->may_mount_proc) - break; + visible = true; + goto found; + next: ; } - up_read(&namespace_sem); +found: + namespace_unlock(); + return visible; } static void *mntns_get(struct task_struct *task) diff --git a/fs/proc/root.c b/fs/proc/root.c index 38bd5d423fcd..45e5fb7da09b 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -110,8 +110,11 @@ static struct dentry *proc_mount(struct file_system_type *fs_type, ns = task_active_pid_ns(current); options = data; - if (!current_user_ns()->may_mount_proc || - !ns_capable(ns->user_ns, CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type)) + return ERR_PTR(-EPERM); + + /* Does the mounter have privilege over the pid namespace? */ + if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN)) return ERR_PTR(-EPERM); } diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index afd83273e6ce..4a2da3a4b1b1 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -112,7 +112,8 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, struct super_block *sb; int error; - if (!(flags & MS_KERNMOUNT) && !current_user_ns()->may_mount_sysfs) + if (!(flags & MS_KERNMOUNT) && !capable(CAP_SYS_ADMIN) && + !fs_fully_visible(fs_type)) return ERR_PTR(-EPERM); info = kzalloc(sizeof(*info), GFP_KERNEL); diff --git a/include/linux/fs.h b/include/linux/fs.h index 981874773e85..3050c620f062 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1897,6 +1897,7 @@ extern int vfs_ustat(dev_t, struct kstatfs *); extern int freeze_super(struct super_block *super); extern int thaw_super(struct super_block *super); extern bool our_mnt(struct vfsmount *mnt); +extern bool fs_fully_visible(struct file_system_type *); extern int current_umask(void); diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index b6b215f13b45..4ce009324933 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -26,8 +26,6 @@ struct user_namespace { kuid_t owner; kgid_t group; unsigned int proc_inum; - bool may_mount_sysfs; - bool may_mount_proc; }; extern struct user_namespace init_user_ns; @@ -84,6 +82,4 @@ static inline void put_user_ns(struct user_namespace *ns) #endif -void update_mnt_policy(struct user_namespace *userns); - #endif /* _LINUX_USER_H */ diff --git a/kernel/user.c b/kernel/user.c index 69b4c3d48cde..5bbb91988e69 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -51,8 +51,6 @@ struct user_namespace init_user_ns = { .owner = GLOBAL_ROOT_UID, .group = GLOBAL_ROOT_GID, .proc_inum = PROC_USER_INIT_INO, - .may_mount_sysfs = true, - .may_mount_proc = true, }; EXPORT_SYMBOL_GPL(init_user_ns); diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index d8c30db06c5b..d58ad1e7a794 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -97,8 +97,6 @@ int create_user_ns(struct cred *new) set_cred_user_ns(new, ns); - update_mnt_policy(ns); - return 0; } -- cgit v1.2.3-71-gd317 From ae77cbc1e7b90473a2b0963bce0e1eb163873214 Mon Sep 17 00:00:00 2001 From: Ken Steele Date: Wed, 7 Aug 2013 12:39:56 -0400 Subject: RAID: add tilegx SIMD implementation of raid6 This change adds TILE-Gx SIMD instructions to the software raid (md), modeling the Altivec implementation. This is only for Syndrome generation; there is more that could be done to improve recovery, as in the recent Intel SSE3 recovery implementation. The code unrolls 8 times; this turns out to be the best on tilegx hardware among the set 1, 2, 4, 8 or 16. The code reads one cache-line of data from each disk, stores P and Q then goes to the next cache-line. The test code in sys/linux/lib/raid6/test reports 2008 MB/s data read rate for syndrome generation using 18 disks (16 data and 2 parity). It was 1512 MB/s before this SIMD optimizations. This is running on 1 core with all the data in cache. This is based on the paper The Mathematics of RAID-6. (http://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf). Signed-off-by: Ken Steele Signed-off-by: Chris Metcalf Signed-off-by: NeilBrown --- include/linux/raid/pq.h | 1 + lib/raid6/Makefile | 6 ++++ lib/raid6/algos.c | 3 ++ lib/raid6/test/Makefile | 7 ++++ lib/raid6/tilegx.uc | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 103 insertions(+) create mode 100644 lib/raid6/tilegx.uc (limited to 'include') diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index 8dfaa2ce2e95..6d5df8adb53a 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -101,6 +101,7 @@ extern const struct raid6_calls raid6_altivec8; extern const struct raid6_calls raid6_avx2x1; extern const struct raid6_calls raid6_avx2x2; extern const struct raid6_calls raid6_avx2x4; +extern const struct raid6_calls raid6_tilegx8; struct raid6_recov_calls { void (*data2)(int, size_t, int, int, void **); diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile index 9f7c184725d7..e5e90219d69e 100644 --- a/lib/raid6/Makefile +++ b/lib/raid6/Makefile @@ -5,6 +5,7 @@ raid6_pq-y += algos.o recov.o tables.o int1.o int2.o int4.o \ raid6_pq-$(CONFIG_X86) += recov_ssse3.o recov_avx2.o mmx.o sse1.o sse2.o avx2.o raid6_pq-$(CONFIG_ALTIVEC) += altivec1.o altivec2.o altivec4.o altivec8.o +raid6_pq-$(CONFIG_TILEGX) += tilegx8.o hostprogs-y += mktables @@ -70,6 +71,11 @@ $(obj)/altivec8.c: UNROLL := 8 $(obj)/altivec8.c: $(src)/altivec.uc $(src)/unroll.awk FORCE $(call if_changed,unroll) +targets += tilegx8.c +$(obj)/tilegx8.c: UNROLL := 8 +$(obj)/tilegx8.c: $(src)/tilegx.uc $(src)/unroll.awk FORCE + $(call if_changed,unroll) + quiet_cmd_mktable = TABLE $@ cmd_mktable = $(obj)/mktables > $@ || ( rm -f $@ && exit 1 ) diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c index 6d7316fe9f30..b9f340180a3b 100644 --- a/lib/raid6/algos.c +++ b/lib/raid6/algos.c @@ -65,6 +65,9 @@ const struct raid6_calls * const raid6_algos[] = { &raid6_altivec2, &raid6_altivec4, &raid6_altivec8, +#endif +#if defined(CONFIG_TILEGX) + &raid6_tilegx8, #endif &raid6_intx1, &raid6_intx2, diff --git a/lib/raid6/test/Makefile b/lib/raid6/test/Makefile index 087332dbf8aa..78f89d807f4e 100644 --- a/lib/raid6/test/Makefile +++ b/lib/raid6/test/Makefile @@ -35,6 +35,9 @@ else OBJS += altivec1.o altivec2.o altivec4.o altivec8.o endif endif +ifeq ($(ARCH),tilegx) +OBJS += tilegx8.o +endif .c.o: $(CC) $(CFLAGS) -c -o $@ $< @@ -85,11 +88,15 @@ int16.c: int.uc ../unroll.awk int32.c: int.uc ../unroll.awk $(AWK) ../unroll.awk -vN=32 < int.uc > $@ +tilegx8.c: tilegx.uc ../unroll.awk + $(AWK) ../unroll.awk -vN=8 < tilegx.uc > $@ + tables.c: mktables ./mktables > tables.c clean: rm -f *.o *.a mktables mktables.c *.uc int*.c altivec*.c tables.c raid6test + rm -f tilegx*.c spotless: clean rm -f *~ diff --git a/lib/raid6/tilegx.uc b/lib/raid6/tilegx.uc new file mode 100644 index 000000000000..e7c29459cbcd --- /dev/null +++ b/lib/raid6/tilegx.uc @@ -0,0 +1,86 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * Copyright 2002 H. Peter Anvin - All Rights Reserved + * Copyright 2012 Tilera Corporation - All Rights Reserved + * + * 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, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * tilegx$#.c + * + * $#-way unrolled TILE-Gx SIMD for RAID-6 math. + * + * This file is postprocessed using unroll.awk. + * + */ + +#include + +/* Create 8 byte copies of constant byte */ +# define NBYTES(x) (__insn_v1addi(0, x)) +# define NSIZE 8 + +/* + * The SHLBYTE() operation shifts each byte left by 1, *not* + * rolling over into the next byte + */ +static inline __attribute_const__ u64 SHLBYTE(u64 v) +{ + /* Vector One Byte Shift Left Immediate. */ + return __insn_v1shli(v, 1); +} + +/* + * The MASK() operation returns 0xFF in any byte for which the high + * bit is 1, 0x00 for any byte for which the high bit is 0. + */ +static inline __attribute_const__ u64 MASK(u64 v) +{ + /* Vector One Byte Shift Right Signed Immediate. */ + return __insn_v1shrsi(v, 7); +} + + +void raid6_tilegx$#_gen_syndrome(int disks, size_t bytes, void **ptrs) +{ + u8 **dptr = (u8 **)ptrs; + u64 *p, *q; + int d, z, z0; + + u64 wd$$, wq$$, wp$$, w1$$, w2$$; + u64 x1d = NBYTES(0x1d); + u64 * z0ptr; + + z0 = disks - 3; /* Highest data disk */ + p = (u64 *)dptr[z0+1]; /* XOR parity */ + q = (u64 *)dptr[z0+2]; /* RS syndrome */ + + z0ptr = (u64 *)&dptr[z0][0]; + for ( d = 0 ; d < bytes ; d += NSIZE*$# ) { + wq$$ = wp$$ = *z0ptr++; + for ( z = z0-1 ; z >= 0 ; z-- ) { + wd$$ = *(u64 *)&dptr[z][d+$$*NSIZE]; + wp$$ = wp$$ ^ wd$$; + w2$$ = MASK(wq$$); + w1$$ = SHLBYTE(wq$$); + w2$$ = w2$$ & x1d; + w1$$ = w1$$ ^ w2$$; + wq$$ = w1$$ ^ wd$$; + } + *p++ = wp$$; + *q++ = wq$$; + } +} + +const struct raid6_calls raid6_tilegx$# = { + raid6_tilegx$#_gen_syndrome, + NULL, + "tilegx$#", + 0 +}; -- cgit v1.2.3-71-gd317 From a2547380393ac82c659b40182b0da8d05a8365f3 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 29 Jul 2013 14:31:45 +0200 Subject: drivers: dma-contiguous: clean source code and prepare for device tree This patch cleans the initialization of dma contiguous framework. The all-in-one dma_declare_contiguous() function is now separated into dma_contiguous_reserve_area() which only steals the the memory from memblock allocator and dma_contiguous_add_device() function, which assigns given device to the specified reserved memory area. This improves the flexibility in defining contiguous memory areas and assigning device to them, because now it is possible to assign more than one device to the given contiguous memory area. Such split in initialization procedure is also required for upcoming device tree support. Signed-off-by: Marek Szyprowski Acked-by: Kyungmin Park Acked-by: Michal Nazarewicz Acked-by: Tomasz Figa --- arch/arm/include/asm/dma-contiguous.h | 1 - arch/x86/include/asm/dma-contiguous.h | 1 - drivers/base/dma-contiguous.c | 119 +++++++++++++--------------------- include/asm-generic/dma-contiguous.h | 28 -------- include/linux/device.h | 2 +- include/linux/dma-contiguous.h | 62 +++++++++++++++++- 6 files changed, 105 insertions(+), 108 deletions(-) delete mode 100644 include/asm-generic/dma-contiguous.h (limited to 'include') diff --git a/arch/arm/include/asm/dma-contiguous.h b/arch/arm/include/asm/dma-contiguous.h index e072bb2ba1b1..4f8e9e5514b1 100644 --- a/arch/arm/include/asm/dma-contiguous.h +++ b/arch/arm/include/asm/dma-contiguous.h @@ -5,7 +5,6 @@ #ifdef CONFIG_DMA_CMA #include -#include void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size); diff --git a/arch/x86/include/asm/dma-contiguous.h b/arch/x86/include/asm/dma-contiguous.h index c09241659971..b4b38bacb404 100644 --- a/arch/x86/include/asm/dma-contiguous.h +++ b/arch/x86/include/asm/dma-contiguous.h @@ -4,7 +4,6 @@ #ifdef __KERNEL__ #include -#include static inline void dma_contiguous_early_fixup(phys_addr_t base, unsigned long size) { } diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c index 0ca54421ce97..99802d6f3c60 100644 --- a/drivers/base/dma-contiguous.c +++ b/drivers/base/dma-contiguous.c @@ -96,7 +96,7 @@ static inline __maybe_unused phys_addr_t cma_early_percent_memory(void) #endif /** - * dma_contiguous_reserve() - reserve area for contiguous memory handling + * dma_contiguous_reserve() - reserve area(s) for contiguous memory handling * @limit: End address of the reserved memory (optional, 0 for any). * * This function reserves memory from early allocator. It should be @@ -124,22 +124,29 @@ void __init dma_contiguous_reserve(phys_addr_t limit) #endif } - if (selected_size) { + if (selected_size && !dma_contiguous_default_area) { pr_debug("%s: reserving %ld MiB for global area\n", __func__, (unsigned long)selected_size / SZ_1M); - dma_declare_contiguous(NULL, selected_size, 0, limit); + dma_contiguous_reserve_area(selected_size, 0, limit, + &dma_contiguous_default_area); } }; static DEFINE_MUTEX(cma_mutex); -static __init int cma_activate_area(unsigned long base_pfn, unsigned long count) +static int __init cma_activate_area(struct cma *cma) { - unsigned long pfn = base_pfn; - unsigned i = count >> pageblock_order; + int bitmap_size = BITS_TO_LONGS(cma->count) * sizeof(long); + unsigned long base_pfn = cma->base_pfn, pfn = base_pfn; + unsigned i = cma->count >> pageblock_order; struct zone *zone; + cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL); + + if (!cma->bitmap) + return -ENOMEM; + WARN_ON_ONCE(!pfn_valid(pfn)); zone = page_zone(pfn_to_page(pfn)); @@ -153,92 +160,53 @@ static __init int cma_activate_area(unsigned long base_pfn, unsigned long count) } init_cma_reserved_pageblock(pfn_to_page(base_pfn)); } while (--i); - return 0; -} - -static __init struct cma *cma_create_area(unsigned long base_pfn, - unsigned long count) -{ - int bitmap_size = BITS_TO_LONGS(count) * sizeof(long); - struct cma *cma; - int ret = -ENOMEM; - - pr_debug("%s(base %08lx, count %lx)\n", __func__, base_pfn, count); - - cma = kmalloc(sizeof *cma, GFP_KERNEL); - if (!cma) - return ERR_PTR(-ENOMEM); - - cma->base_pfn = base_pfn; - cma->count = count; - cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL); - if (!cma->bitmap) - goto no_mem; - - ret = cma_activate_area(base_pfn, count); - if (ret) - goto error; - - pr_debug("%s: returned %p\n", __func__, (void *)cma); - return cma; - -error: - kfree(cma->bitmap); -no_mem: - kfree(cma); - return ERR_PTR(ret); + return 0; } -static struct cma_reserved { - phys_addr_t start; - unsigned long size; - struct device *dev; -} cma_reserved[MAX_CMA_AREAS] __initdata; -static unsigned cma_reserved_count __initdata; +static struct cma cma_areas[MAX_CMA_AREAS]; +static unsigned cma_area_count; static int __init cma_init_reserved_areas(void) { - struct cma_reserved *r = cma_reserved; - unsigned i = cma_reserved_count; - - pr_debug("%s()\n", __func__); + int i; - for (; i; --i, ++r) { - struct cma *cma; - cma = cma_create_area(PFN_DOWN(r->start), - r->size >> PAGE_SHIFT); - if (!IS_ERR(cma)) - dev_set_cma_area(r->dev, cma); + for (i = 0; i < cma_area_count; i++) { + int ret = cma_activate_area(&cma_areas[i]); + if (ret) + return ret; } + return 0; } core_initcall(cma_init_reserved_areas); /** - * dma_declare_contiguous() - reserve area for contiguous memory handling - * for particular device - * @dev: Pointer to device structure. - * @size: Size of the reserved memory. - * @base: Start address of the reserved memory (optional, 0 for any). + * dma_contiguous_reserve_area() - reserve custom contiguous area + * @size: Size of the reserved area (in bytes), + * @base: Base address of the reserved area optional, use 0 for any * @limit: End address of the reserved memory (optional, 0 for any). + * @res_cma: Pointer to store the created cma region. * - * This function reserves memory for specified device. It should be - * called by board specific code when early allocator (memblock or bootmem) - * is still activate. + * This function reserves memory from early allocator. It should be + * called by arch specific code once the early allocator (memblock or bootmem) + * has been activated and all other subsystems have already allocated/reserved + * memory. This function allows to create custom reserved areas for specific + * devices. */ -int __init dma_declare_contiguous(struct device *dev, phys_addr_t size, - phys_addr_t base, phys_addr_t limit) +int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, + phys_addr_t limit, struct cma **res_cma) { - struct cma_reserved *r = &cma_reserved[cma_reserved_count]; + struct cma *cma = &cma_areas[cma_area_count]; phys_addr_t alignment; + int ret = 0; pr_debug("%s(size %lx, base %08lx, limit %08lx)\n", __func__, (unsigned long)size, (unsigned long)base, (unsigned long)limit); /* Sanity checks */ - if (cma_reserved_count == ARRAY_SIZE(cma_reserved)) { + if (cma_area_count == ARRAY_SIZE(cma_areas)) { pr_err("Not enough slots for CMA reserved regions!\n"); return -ENOSPC; } @@ -256,7 +224,7 @@ int __init dma_declare_contiguous(struct device *dev, phys_addr_t size, if (base) { if (memblock_is_region_reserved(base, size) || memblock_reserve(base, size) < 0) { - base = -EBUSY; + ret = -EBUSY; goto err; } } else { @@ -266,7 +234,7 @@ int __init dma_declare_contiguous(struct device *dev, phys_addr_t size, */ phys_addr_t addr = __memblock_alloc_base(size, alignment, limit); if (!addr) { - base = -ENOMEM; + ret = -ENOMEM; goto err; } else { base = addr; @@ -277,10 +245,11 @@ int __init dma_declare_contiguous(struct device *dev, phys_addr_t size, * Each reserved area must be initialised later, when more kernel * subsystems (like slab allocator) are available. */ - r->start = base; - r->size = size; - r->dev = dev; - cma_reserved_count++; + cma->base_pfn = PFN_DOWN(base); + cma->count = size >> PAGE_SHIFT; + *res_cma = cma; + cma_area_count++; + pr_info("CMA: reserved %ld MiB at %08lx\n", (unsigned long)size / SZ_1M, (unsigned long)base); @@ -289,7 +258,7 @@ int __init dma_declare_contiguous(struct device *dev, phys_addr_t size, return 0; err: pr_err("CMA: failed to reserve %ld MiB\n", (unsigned long)size / SZ_1M); - return base; + return ret; } /** diff --git a/include/asm-generic/dma-contiguous.h b/include/asm-generic/dma-contiguous.h deleted file mode 100644 index 294b1e755ab2..000000000000 --- a/include/asm-generic/dma-contiguous.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef ASM_DMA_CONTIGUOUS_H -#define ASM_DMA_CONTIGUOUS_H - -#ifdef __KERNEL__ -#ifdef CONFIG_CMA - -#include -#include - -static inline struct cma *dev_get_cma_area(struct device *dev) -{ - if (dev && dev->cma_area) - return dev->cma_area; - return dma_contiguous_default_area; -} - -static inline void dev_set_cma_area(struct device *dev, struct cma *cma) -{ - if (dev) - dev->cma_area = cma; - if (!dev && !dma_contiguous_default_area) - dma_contiguous_default_area = cma; -} - -#endif -#endif - -#endif diff --git a/include/linux/device.h b/include/linux/device.h index bcf8c0d4cd98..9200cfd75f15 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -711,7 +711,7 @@ struct device { struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ -#ifdef CONFIG_CMA +#ifdef CONFIG_DMA_CMA struct cma *cma_area; /* contiguous memory area for dma allocations */ #endif diff --git a/include/linux/dma-contiguous.h b/include/linux/dma-contiguous.h index 00141d3325fe..3b28f937d959 100644 --- a/include/linux/dma-contiguous.h +++ b/include/linux/dma-contiguous.h @@ -67,9 +67,53 @@ struct device; extern struct cma *dma_contiguous_default_area; +static inline struct cma *dev_get_cma_area(struct device *dev) +{ + if (dev && dev->cma_area) + return dev->cma_area; + return dma_contiguous_default_area; +} + +static inline void dev_set_cma_area(struct device *dev, struct cma *cma) +{ + if (dev) + dev->cma_area = cma; +} + +static inline void dma_contiguous_set_default(struct cma *cma) +{ + dma_contiguous_default_area = cma; +} + void dma_contiguous_reserve(phys_addr_t addr_limit); -int dma_declare_contiguous(struct device *dev, phys_addr_t size, - phys_addr_t base, phys_addr_t limit); + +int __init dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, + phys_addr_t limit, struct cma **res_cma); + +/** + * dma_declare_contiguous() - reserve area for contiguous memory handling + * for particular device + * @dev: Pointer to device structure. + * @size: Size of the reserved memory. + * @base: Start address of the reserved memory (optional, 0 for any). + * @limit: End address of the reserved memory (optional, 0 for any). + * + * This function reserves memory for specified device. It should be + * called by board specific code when early allocator (memblock or bootmem) + * is still activate. + */ + +static inline int dma_declare_contiguous(struct device *dev, phys_addr_t size, + phys_addr_t base, phys_addr_t limit) +{ + struct cma *cma; + int ret; + ret = dma_contiguous_reserve_area(size, base, limit, &cma); + if (ret == 0) + dev_set_cma_area(dev, cma); + + return ret; +} struct page *dma_alloc_from_contiguous(struct device *dev, int count, unsigned int order); @@ -80,8 +124,22 @@ bool dma_release_from_contiguous(struct device *dev, struct page *pages, #define MAX_CMA_AREAS (0) +static inline struct cma *dev_get_cma_area(struct device *dev) +{ + return NULL; +} + +static inline void dev_set_cma_area(struct device *dev, struct cma *cma) { } + +static inline void dma_contiguous_set_default(struct cma *cma) { } + static inline void dma_contiguous_reserve(phys_addr_t limit) { } +static inline int dma_contiguous_reserve_area(phys_addr_t size, phys_addr_t base, + phys_addr_t limit, struct cma **res_cma) { + return -ENOSYS; +} + static inline int dma_declare_contiguous(struct device *dev, phys_addr_t size, phys_addr_t base, phys_addr_t limit) -- cgit v1.2.3-71-gd317 From 57d74bcf3072b65bde5aa540cedc976a75c48e5c Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 26 Aug 2013 14:41:56 +0200 Subject: drivers: of: add function to scan fdt nodes given by path Add a function to scan the flattened device-tree starting from the node given by the path. It is used to extract information (like reserved memory), which is required on early boot before we can unflatten the tree. Signed-off-by: Marek Szyprowski Acked-by: Michal Nazarewicz Acked-by: Tomasz Figa Reviewed-by: Rob Herring --- drivers/of/fdt.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_fdt.h | 3 ++ 2 files changed, 79 insertions(+) (limited to 'include') diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 6bb7cf2de556..585b1a67cfbe 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -543,6 +543,82 @@ int __init of_flat_dt_match(unsigned long node, const char *const *compat) return of_fdt_match(initial_boot_params, node, compat); } +struct fdt_scan_status { + const char *name; + int namelen; + int depth; + int found; + int (*iterator)(unsigned long node, const char *uname, int depth, void *data); + void *data; +}; + +/** + * fdt_scan_node_by_path - iterator for of_scan_flat_dt_by_path function + */ +static int __init fdt_scan_node_by_path(unsigned long node, const char *uname, + int depth, void *data) +{ + struct fdt_scan_status *st = data; + + /* + * if scan at the requested fdt node has been completed, + * return -ENXIO to abort further scanning + */ + if (depth <= st->depth) + return -ENXIO; + + /* requested fdt node has been found, so call iterator function */ + if (st->found) + return st->iterator(node, uname, depth, st->data); + + /* check if scanning automata is entering next level of fdt nodes */ + if (depth == st->depth + 1 && + strncmp(st->name, uname, st->namelen) == 0 && + uname[st->namelen] == 0) { + st->depth += 1; + if (st->name[st->namelen] == 0) { + st->found = 1; + } else { + const char *next = st->name + st->namelen + 1; + st->name = next; + st->namelen = strcspn(next, "/"); + } + return 0; + } + + /* scan next fdt node */ + return 0; +} + +/** + * of_scan_flat_dt_by_path - scan flattened tree blob and call callback on each + * child of the given path. + * @path: path to start searching for children + * @it: callback function + * @data: context data pointer + * + * This function is used to scan the flattened device-tree starting from the + * node given by path. It is used to extract information (like reserved + * memory), which is required on ealy boot before we can unflatten the tree. + */ +int __init of_scan_flat_dt_by_path(const char *path, + int (*it)(unsigned long node, const char *name, int depth, void *data), + void *data) +{ + struct fdt_scan_status st = {path, 0, -1, 0, it, data}; + int ret = 0; + + if (initial_boot_params) + ret = of_scan_flat_dt(fdt_scan_node_by_path, &st); + + if (!st.found) + return -ENOENT; + else if (ret == -ENXIO) /* scan has been completed */ + return 0; + else + return ret; +} + #ifdef CONFIG_BLK_DEV_INITRD /** * early_init_dt_check_for_initrd - Decode initrd location from flat tree diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index ed136ad698ce..19f26f8d2202 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -90,6 +90,9 @@ extern void *of_get_flat_dt_prop(unsigned long node, const char *name, extern int of_flat_dt_is_compatible(unsigned long node, const char *name); extern int of_flat_dt_match(unsigned long node, const char *const *matches); extern unsigned long of_get_flat_dt_root(void); +extern int of_scan_flat_dt_by_path(const char *path, + int (*it)(unsigned long node, const char *name, int depth, void *data), + void *data); extern int early_init_dt_scan_chosen(unsigned long node, const char *uname, int depth, void *data); -- cgit v1.2.3-71-gd317 From f961bd3516e4f699bbacff5d7f5247d6d87c59f0 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 22 Aug 2013 14:51:08 +0200 Subject: HID: detect Win 8 multitouch devices in core Detecting Win 8 multitouch devices in core allows us to set quirks before the device is parsed through hid_hw_start(). It also simplifies the detection of those devices in hid-multitouch and makes the handling of those devices cleaner. As Win 8 multitouch panels are in the group multitouch and rely on a special feature to be detected, this patch adds a bitfield in the parser. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Tested-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 16 ++++++++++++++++ drivers/hid/hid-multitouch.c | 24 +++++++++++------------- include/linux/hid.h | 4 ++++ 3 files changed, 31 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ddd95f3e33c0..660dce964162 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -685,6 +685,13 @@ static void hid_scan_input_usage(struct hid_parser *parser, u32 usage) hid->group = HID_GROUP_MULTITOUCH; } +static void hid_scan_feature_usage(struct hid_parser *parser, u32 usage) +{ + if (usage == 0xff0000c5 && parser->global.report_count == 256 && + parser->global.report_size == 8) + parser->scan_flags |= HID_SCAN_FLAG_MT_WIN_8; +} + static void hid_scan_collection(struct hid_parser *parser, unsigned type) { struct hid_device *hid = parser->device; @@ -714,6 +721,8 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) case HID_MAIN_ITEM_TAG_OUTPUT: break; case HID_MAIN_ITEM_TAG_FEATURE: + for (i = 0; i < parser->local.usage_index; i++) + hid_scan_feature_usage(parser, parser->local.usage[i]); break; } @@ -757,6 +766,13 @@ static int hid_scan_report(struct hid_device *hid) while ((start = fetch_item(start, end, &item)) != NULL) dispatch_type[item.type](parser, &item); + /* + * Handle special flags set during scanning. + */ + if ((parser->scan_flags & HID_SCAN_FLAG_MT_WIN_8) && + (hid->group == HID_GROUP_MULTITOUCH)) + hid->group = HID_GROUP_MULTITOUCH_WIN_8; + vfree(parser); return 0; } diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 0fe00e2552f2..c28ef86c7c67 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -133,6 +133,7 @@ static void mt_post_parse(struct mt_device *td); #define MT_CLS_NSMU 0x000a #define MT_CLS_DUAL_CONTACT_NUMBER 0x0010 #define MT_CLS_DUAL_CONTACT_ID 0x0011 +#define MT_CLS_WIN_8 0x0012 /* vendor specific classes */ #define MT_CLS_3M 0x0101 @@ -205,6 +206,11 @@ static struct mt_class mt_classes[] = { MT_QUIRK_CONTACT_CNT_ACCURATE | MT_QUIRK_SLOT_IS_CONTACTID, .maxcontacts = 2 }, + { .name = MT_CLS_WIN_8, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_IGNORE_DUPLICATES | + MT_QUIRK_HOVERING | + MT_QUIRK_CONTACT_CNT_ACCURATE }, /* * vendor specific classes @@ -332,19 +338,6 @@ static void mt_feature_mapping(struct hid_device *hdev, td->maxcontacts = td->mtclass.maxcontacts; break; - case 0xff0000c5: - if (field->report_count == 256 && field->report_size == 8) { - /* Win 8 devices need special quirks */ - __s32 *quirks = &td->mtclass.quirks; - *quirks |= MT_QUIRK_ALWAYS_VALID; - *quirks |= MT_QUIRK_IGNORE_DUPLICATES; - *quirks |= MT_QUIRK_HOVERING; - *quirks |= MT_QUIRK_CONTACT_CNT_ACCURATE; - *quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; - *quirks &= ~MT_QUIRK_VALID_IS_INRANGE; - *quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE; - } - break; } } @@ -1346,6 +1339,11 @@ static const struct hid_device_id mt_devices[] = { /* Generic MT device */ { HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) }, + + /* Generic Win 8 certified MT device */ + { .driver_data = MT_CLS_WIN_8, + HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH_WIN_8, + HID_ANY_ID, HID_ANY_ID) }, { } }; MODULE_DEVICE_TABLE(hid, mt_devices); diff --git a/include/linux/hid.h b/include/linux/hid.h index 0c48991b0402..cef1e9b86cc4 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -295,6 +295,7 @@ struct hid_item { #define HID_GROUP_GENERIC 0x0001 #define HID_GROUP_MULTITOUCH 0x0002 #define HID_GROUP_SENSOR_HUB 0x0003 +#define HID_GROUP_MULTITOUCH_WIN_8 0x0004 /* * This is the global environment of the parser. This information is @@ -532,6 +533,8 @@ static inline void hid_set_drvdata(struct hid_device *hdev, void *data) #define HID_GLOBAL_STACK_SIZE 4 #define HID_COLLECTION_STACK_SIZE 4 +#define HID_SCAN_FLAG_MT_WIN_8 0x00000001 + struct hid_parser { struct hid_global global; struct hid_global global_stack[HID_GLOBAL_STACK_SIZE]; @@ -540,6 +543,7 @@ struct hid_parser { unsigned collection_stack[HID_COLLECTION_STACK_SIZE]; unsigned collection_stack_ptr; struct hid_device *device; + unsigned scan_flags; }; struct hid_class_descriptor { -- cgit v1.2.3-71-gd317 From 595e9276ce68791317484ec7f0f9f2e0457c3b34 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 22 Aug 2013 14:51:09 +0200 Subject: HID: do not init input reports for Win 8 multitouch devices Some multitouch screens do not like to be polled for input reports. However, the Win8 spec says that all touches should be sent during each report, making the initialization of reports unnecessary. The Win7 spec is less precise, so do not use this for those devices. Add the quirk HID_QUIRK_NO_INIT_INPUT_REPORTS so that we do not have to introduce a quirk for each problematic device. This quirk makes the driver behave the same way the Win 8 does. It actually retrieves the features, but not the inputs. Signed-off-by: Benjamin Tissoires Reviewed-by: Henrik Rydberg Tested-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 12 ++++++++++++ drivers/hid/usbhid/hid-core.c | 11 ++++++++--- include/linux/hid.h | 1 + 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index c28ef86c7c67..ac28f08c3866 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -951,6 +951,18 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) hdev->quirks |= HID_QUIRK_MULTI_INPUT; hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT; + /* + * Handle special quirks for Windows 8 certified devices. + */ + if (id->group == HID_GROUP_MULTITOUCH_WIN_8) + /* + * Some multitouch screens do not like to be polled for input + * reports. Fortunately, the Win8 spec says that all touches + * should be sent during each report, making the initialization + * of input reports unnecessary. + */ + hdev->quirks |= HID_QUIRK_NO_INIT_INPUT_REPORTS; + td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL); if (!td) { dev_err(&hdev->dev, "cannot allocate multitouch data\n"); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 99418285222c..55ea9c40140e 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -807,12 +807,17 @@ void usbhid_init_reports(struct hid_device *hid) { struct hid_report *report; struct usbhid_device *usbhid = hid->driver_data; + struct hid_report_enum *report_enum; int err, ret; - list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list) - usbhid_submit_report(hid, report, USB_DIR_IN); + if (!(hid->quirks & HID_QUIRK_NO_INIT_INPUT_REPORTS)) { + report_enum = &hid->report_enum[HID_INPUT_REPORT]; + list_for_each_entry(report, &report_enum->report_list, list) + usbhid_submit_report(hid, report, USB_DIR_IN); + } - list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list) + report_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(report, &report_enum->report_list, list) usbhid_submit_report(hid, report, USB_DIR_IN); err = 0; diff --git a/include/linux/hid.h b/include/linux/hid.h index cef1e9b86cc4..bc132d2a20aa 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -283,6 +283,7 @@ struct hid_item { #define HID_QUIRK_MULTI_INPUT 0x00000040 #define HID_QUIRK_HIDINPUT_FORCE 0x00000080 #define HID_QUIRK_NO_EMPTY_INPUT 0x00000100 +#define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 #define HID_QUIRK_NO_INIT_REPORTS 0x20000000 -- cgit v1.2.3-71-gd317 From 9d8eab7af79cb4ce2de5de39f82c455b1f796963 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 26 Aug 2013 14:43:10 +0200 Subject: drivers: of: add initialization code for dma reserved memory This patch adds device tree support for contiguous and reserved memory regions defined in device tree. Large memory blocks can be reliably reserved only during early boot. This must happen before the whole memory management subsystem is initialized, because we need to ensure that the given contiguous blocks are not yet allocated by kernel. Also it must happen before kernel mappings for the whole low memory are created, to ensure that there will be no mappings (for reserved blocks) or mapping with special properties can be created (for CMA blocks). This all happens before device tree structures are unflattened, so we need to get reserved memory layout directly from fdt. Later, those reserved memory regions are assigned to devices on each device structure initialization. Signed-off-by: Marek Szyprowski Acked-by: Kyungmin Park Acked-by: Michal Nazarewicz Acked-by: Tomasz Figa Acked-by: Stephen Warren Reviewed-by: Rob Herring --- Documentation/devicetree/bindings/memory.txt | 168 +++++++++++++++++++++++++ drivers/of/Kconfig | 6 + drivers/of/Makefile | 1 + drivers/of/of_reserved_mem.c | 175 +++++++++++++++++++++++++++ drivers/of/platform.c | 4 + include/linux/of_reserved_mem.h | 14 +++ 6 files changed, 368 insertions(+) create mode 100644 Documentation/devicetree/bindings/memory.txt create mode 100644 drivers/of/of_reserved_mem.c create mode 100644 include/linux/of_reserved_mem.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/memory.txt b/Documentation/devicetree/bindings/memory.txt new file mode 100644 index 000000000000..eb2469365593 --- /dev/null +++ b/Documentation/devicetree/bindings/memory.txt @@ -0,0 +1,168 @@ +*** Memory binding *** + +The /memory node provides basic information about the address and size +of the physical memory. This node is usually filled or updated by the +bootloader, depending on the actual memory configuration of the given +hardware. + +The memory layout is described by the following node: + +/ { + #address-cells = <(n)>; + #size-cells = <(m)>; + memory { + device_type = "memory"; + reg = <(baseaddr1) (size1) + (baseaddr2) (size2) + ... + (baseaddrN) (sizeN)>; + }; + ... +}; + +A memory node follows the typical device tree rules for "reg" property: +n: number of cells used to store base address value +m: number of cells used to store size value +baseaddrX: defines a base address of the defined memory bank +sizeX: the size of the defined memory bank + + +More than one memory bank can be defined. + + +*** Reserved memory regions *** + +In /memory/reserved-memory node one can create child nodes describing +particular reserved (excluded from normal use) memory regions. Such +memory regions are usually designed for the special usage by various +device drivers. A good example are contiguous memory allocations or +memory sharing with other operating system on the same hardware board. +Those special memory regions might depend on the board configuration and +devices used on the target system. + +Parameters for each memory region can be encoded into the device tree +with the following convention: + +[(label):] (name) { + compatible = "linux,contiguous-memory-region", "reserved-memory-region"; + reg = <(address) (size)>; + (linux,default-contiguous-region); +}; + +compatible: one or more of: + - "linux,contiguous-memory-region" - enables binding of this + region to Contiguous Memory Allocator (special region for + contiguous memory allocations, shared with movable system + memory, Linux kernel-specific). + - "reserved-memory-region" - compatibility is defined, given + region is assigned for exclusive usage for by the respective + devices. + +reg: standard property defining the base address and size of + the memory region + +linux,default-contiguous-region: property indicating that the region + is the default region for all contiguous memory + allocations, Linux specific (optional) + +It is optional to specify the base address, so if one wants to use +autoconfiguration of the base address, '0' can be specified as a base +address in the 'reg' property. + +The /memory/reserved-memory node must contain the same #address-cells +and #size-cells value as the root node. + + +*** Device node's properties *** + +Once regions in the /memory/reserved-memory node have been defined, they +may be referenced by other device nodes. Bindings that wish to reference +memory regions should explicitly document their use of the following +property: + +memory-region = <&phandle_to_defined_region>; + +This property indicates that the device driver should use the memory +region pointed by the given phandle. + + +*** Example *** + +This example defines a memory consisting of 4 memory banks. 3 contiguous +regions are defined for Linux kernel, one default of all device drivers +(named contig_mem, placed at 0x72000000, 64MiB), one dedicated to the +framebuffer device (labelled display_mem, placed at 0x78000000, 8MiB) +and one for multimedia processing (labelled multimedia_mem, placed at +0x77000000, 64MiB). 'display_mem' region is then assigned to fb@12300000 +device for DMA memory allocations (Linux kernel drivers will use CMA is +available or dma-exclusive usage otherwise). 'multimedia_mem' is +assigned to scaler@12500000 and codec@12600000 devices for contiguous +memory allocations when CMA driver is enabled. + +The reason for creating a separate region for framebuffer device is to +match the framebuffer base address to the one configured by bootloader, +so once Linux kernel drivers starts no glitches on the displayed boot +logo appears. Scaller and codec drivers should share the memory +allocations. + +/ { + #address-cells = <1>; + #size-cells = <1>; + + /* ... */ + + memory { + reg = <0x40000000 0x10000000 + 0x50000000 0x10000000 + 0x60000000 0x10000000 + 0x70000000 0x10000000>; + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + + /* + * global autoconfigured region for contiguous allocations + * (used only with Contiguous Memory Allocator) + */ + contig_region@0 { + compatible = "linux,contiguous-memory-region"; + reg = <0x0 0x4000000>; + linux,default-contiguous-region; + }; + + /* + * special region for framebuffer + */ + display_region: region@78000000 { + compatible = "linux,contiguous-memory-region", "reserved-memory-region"; + reg = <0x78000000 0x800000>; + }; + + /* + * special region for multimedia processing devices + */ + multimedia_region: region@77000000 { + compatible = "linux,contiguous-memory-region"; + reg = <0x77000000 0x4000000>; + }; + }; + }; + + /* ... */ + + fb0: fb@12300000 { + status = "okay"; + memory-region = <&display_region>; + }; + + scaler: scaler@12500000 { + status = "okay"; + memory-region = <&multimedia_region>; + }; + + codec: codec@12600000 { + status = "okay"; + memory-region = <&multimedia_region>; + }; +}; diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 80e5c13b930d..730965157b2c 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -80,4 +80,10 @@ config OF_MTD depends on MTD def_bool y +config OF_RESERVED_MEM + depends on OF_FLATTREE && (DMA_CMA || (HAVE_GENERIC_DMA_COHERENT && HAVE_MEMBLOCK)) + def_bool y + help + Initialization code for DMA reserved memory + endmenu # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 1f9c0c492ef9..e7e3322679a8 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o +obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c new file mode 100644 index 000000000000..a754b84ba016 --- /dev/null +++ b/drivers/of/of_reserved_mem.c @@ -0,0 +1,175 @@ +/* + * Device tree based initialization code for reserved memory. + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Author: Marek Szyprowski + * + * 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; either version 2 of the + * License or (at your optional) any later version of the license. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_RESERVED_REGIONS 16 +struct reserved_mem { + phys_addr_t base; + unsigned long size; + struct cma *cma; + char name[32]; +}; +static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; +static int reserved_mem_count; + +static int __init fdt_scan_reserved_mem(unsigned long node, const char *uname, + int depth, void *data) +{ + struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; + phys_addr_t base, size; + int is_cma, is_reserved; + unsigned long len; + const char *status; + __be32 *prop; + + is_cma = IS_ENABLED(CONFIG_DMA_CMA) && + of_flat_dt_is_compatible(node, "linux,contiguous-memory-region"); + is_reserved = of_flat_dt_is_compatible(node, "reserved-memory-region"); + + if (!is_reserved && !is_cma) { + /* ignore node and scan next one */ + return 0; + } + + status = of_get_flat_dt_prop(node, "status", &len); + if (status && strcmp(status, "okay") != 0) { + /* ignore disabled node nad scan next one */ + return 0; + } + + prop = of_get_flat_dt_prop(node, "reg", &len); + if (!prop || (len < (dt_root_size_cells + dt_root_addr_cells) * + sizeof(__be32))) { + pr_err("Reserved mem: node %s, incorrect \"reg\" property\n", + uname); + /* ignore node and scan next one */ + return 0; + } + base = dt_mem_next_cell(dt_root_addr_cells, &prop); + size = dt_mem_next_cell(dt_root_size_cells, &prop); + + if (!size) { + /* ignore node and scan next one */ + return 0; + } + + pr_info("Reserved mem: found %s, memory base %lx, size %ld MiB\n", + uname, (unsigned long)base, (unsigned long)size / SZ_1M); + + if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) + return -ENOSPC; + + rmem->base = base; + rmem->size = size; + strlcpy(rmem->name, uname, sizeof(rmem->name)); + + if (is_cma) { + struct cma *cma; + if (dma_contiguous_reserve_area(size, base, 0, &cma) == 0) { + rmem->cma = cma; + reserved_mem_count++; + if (of_get_flat_dt_prop(node, + "linux,default-contiguous-region", + NULL)) + dma_contiguous_set_default(cma); + } + } else if (is_reserved) { + if (memblock_remove(base, size) == 0) + reserved_mem_count++; + else + pr_err("Failed to reserve memory for %s\n", uname); + } + + return 0; +} + +static struct reserved_mem *get_dma_memory_region(struct device *dev) +{ + struct device_node *node; + const char *name; + int i; + + node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!node) + return NULL; + + name = kbasename(node->full_name); + for (i = 0; i < reserved_mem_count; i++) + if (strcmp(name, reserved_mem[i].name) == 0) + return &reserved_mem[i]; + return NULL; +} + +/** + * of_reserved_mem_device_init() - assign reserved memory region to given device + * + * This function assign memory region pointed by "memory-region" device tree + * property to the given device. + */ +void of_reserved_mem_device_init(struct device *dev) +{ + struct reserved_mem *region = get_dma_memory_region(dev); + if (!region) + return; + + if (region->cma) { + dev_set_cma_area(dev, region->cma); + pr_info("Assigned CMA %s to %s device\n", region->name, + dev_name(dev)); + } else { + if (dma_declare_coherent_memory(dev, region->base, region->base, + region->size, DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE) != 0) + pr_info("Declared reserved memory %s to %s device\n", + region->name, dev_name(dev)); + } +} + +/** + * of_reserved_mem_device_release() - release reserved memory device structures + * + * This function releases structures allocated for memory region handling for + * the given device. + */ +void of_reserved_mem_device_release(struct device *dev) +{ + struct reserved_mem *region = get_dma_memory_region(dev); + if (!region && !region->cma) + dma_release_declared_memory(dev); +} + +/** + * early_init_dt_scan_reserved_mem() - create reserved memory regions + * + * This function grabs memory from early allocator for device exclusive use + * defined in device tree structures. It should be called by arch specific code + * once the early allocator (memblock) has been activated and all other + * subsystems have already allocated/reserved memory. + */ +void __init early_init_dt_scan_reserved_mem(void) +{ + of_scan_flat_dt_by_path("/memory/reserved-memory", + fdt_scan_reserved_mem, NULL); +} diff --git a/drivers/of/platform.c b/drivers/of/platform.c index e0a6514ab46c..eeca8a596973 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -21,6 +21,7 @@ #include #include #include +#include #include const struct of_device_id of_default_bus_match_table[] = { @@ -218,6 +219,8 @@ struct platform_device *of_platform_device_create_pdata( dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data; + of_reserved_mem_device_init(&dev->dev); + /* We do not fill the DMA ops for platform devices by default. * This is currently the responsibility of the platform code * to do such, possibly using a device notifier @@ -225,6 +228,7 @@ struct platform_device *of_platform_device_create_pdata( if (of_device_add(dev) != 0) { platform_device_put(dev); + of_reserved_mem_device_release(&dev->dev); return NULL; } diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h new file mode 100644 index 000000000000..c84128255814 --- /dev/null +++ b/include/linux/of_reserved_mem.h @@ -0,0 +1,14 @@ +#ifndef __OF_RESERVED_MEM_H +#define __OF_RESERVED_MEM_H + +#ifdef CONFIG_OF_RESERVED_MEM +void of_reserved_mem_device_init(struct device *dev); +void of_reserved_mem_device_release(struct device *dev); +void early_init_dt_scan_reserved_mem(void); +#else +static inline void of_reserved_mem_device_init(struct device *dev) { } +static inline void of_reserved_mem_device_release(struct device *dev) { } +static inline void early_init_dt_scan_reserved_mem(void) { } +#endif + +#endif /* __OF_RESERVED_MEM_H */ -- cgit v1.2.3-71-gd317 From ca8b387803072a16baf6d8090591b10bfdf4e253 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 10 Jul 2013 12:09:47 +0200 Subject: DMA: shdma: support the new CHCLR register layout On newer r-car SoCs the CHCLR register only contains one bit per channel, to which a 1 has to be written to reset the channel. Older SoC versions had one CHCLR register per channel, to which a 0 must be written to reset the channel and clear its buffers. This patch adds support for the newer layout. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma.c | 14 ++++++++++++-- include/linux/sh_dma.h | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c index 9ee1272604b4..53bd6308fc2f 100644 --- a/drivers/dma/sh/shdma.c +++ b/drivers/dma/sh/shdma.c @@ -49,12 +49,22 @@ static DEFINE_SPINLOCK(sh_dmae_lock); static LIST_HEAD(sh_dmae_devices); +/* + * Different DMAC implementations provide different ways to clear DMA channels: + * (1) none - no CHCLR registers are available + * (2) one CHCLR register per channel - 0 has to be written to it to clear + * channel buffers + * (3) one CHCLR per several channels - 1 has to be written to the bit, + * corresponding to the specific channel to reset it + */ static void channel_clear(struct sh_dmae_chan *sh_dc) { struct sh_dmae_device *shdev = to_sh_dev(sh_dc); + const struct sh_dmae_channel *chan_pdata = shdev->pdata->channel + + sh_dc->shdma_chan.id; + u32 val = shdev->pdata->chclr_bitwise ? 1 << chan_pdata->chclr_bit : 0; - __raw_writel(0, shdev->chan_reg + - shdev->pdata->channel[sh_dc->shdma_chan.id].chclr_offset); + __raw_writel(val, shdev->chan_reg + chan_pdata->chclr_offset); } static void sh_dmae_writel(struct sh_dmae_chan *sh_dc, u32 data, u32 reg) diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index 4e83f3e034f3..776ed9d682f4 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -33,13 +33,44 @@ struct sh_dmae_slave_config { char mid_rid; }; +/** + * struct sh_dmae_channel - DMAC channel platform data + * @offset: register offset within the main IOMEM resource + * @dmars: channel DMARS register offset + * @chclr_offset: channel CHCLR register offset + * @dmars_bit: channel DMARS field offset within the register + * @chclr_bit: bit position, to be set to reset the channel + */ struct sh_dmae_channel { unsigned int offset; unsigned int dmars; - unsigned int dmars_bit; unsigned int chclr_offset; + unsigned char dmars_bit; + unsigned char chclr_bit; }; +/** + * struct sh_dmae_pdata - DMAC platform data + * @slave: array of slaves + * @slave_num: number of slaves in the above array + * @channel: array of DMA channels + * @channel_num: number of channels in the above array + * @ts_low_shift: shift of the low part of the TS field + * @ts_low_mask: low TS field mask + * @ts_high_shift: additional shift of the high part of the TS field + * @ts_high_mask: high TS field mask + * @ts_shift: array of Transfer Size shifts, indexed by TS value + * @ts_shift_num: number of shifts in the above array + * @dmaor_init: DMAOR initialisation value + * @chcr_offset: CHCR address offset + * @chcr_ie_bit: CHCR Interrupt Enable bit + * @dmaor_is_32bit: DMAOR is a 32-bit register + * @needs_tend_set: the TEND register has to be set + * @no_dmars: DMAC has no DMARS registers + * @chclr_present: DMAC has one or several CHCLR registers + * @chclr_bitwise: channel CHCLR registers are bitwise + * @slave_only: DMAC cannot be used for MEMCPY + */ struct sh_dmae_pdata { const struct sh_dmae_slave_config *slave; int slave_num; @@ -59,6 +90,7 @@ struct sh_dmae_pdata { unsigned int needs_tend_set:1; unsigned int no_dmars:1; unsigned int chclr_present:1; + unsigned int chclr_bitwise:1; unsigned int slave_only:1; }; -- cgit v1.2.3-71-gd317 From c1c63a14f4f2419d093acd7164eccdff315baa86 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 2 Jul 2013 17:45:55 +0200 Subject: DMA: shdma: switch to managed resource allocation Switch shdma to using devm_* managed functions for allocation of memory, requesting IRQs, mapping IO resources etc. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- drivers/dma/sh/shdma-base.c | 11 ++------ drivers/dma/sh/shdma.c | 66 +++++++++------------------------------------ drivers/dma/sh/sudmac.c | 1 - include/linux/shdma-base.h | 1 - 4 files changed, 15 insertions(+), 64 deletions(-) (limited to 'include') diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 28ca36121631..c5ea256c2819 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -831,8 +831,8 @@ static irqreturn_t chan_irqt(int irq, void *dev) int shdma_request_irq(struct shdma_chan *schan, int irq, unsigned long flags, const char *name) { - int ret = request_threaded_irq(irq, chan_irq, chan_irqt, - flags, name, schan); + int ret = devm_request_threaded_irq(schan->dev, irq, chan_irq, + chan_irqt, flags, name, schan); schan->irq = ret < 0 ? ret : irq; @@ -840,13 +840,6 @@ int shdma_request_irq(struct shdma_chan *schan, int irq, } EXPORT_SYMBOL(shdma_request_irq); -void shdma_free_irq(struct shdma_chan *schan) -{ - if (schan->irq >= 0) - free_irq(schan->irq, schan); -} -EXPORT_SYMBOL(shdma_free_irq); - void shdma_chan_probe(struct shdma_dev *sdev, struct shdma_chan *schan, int id) { diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c index 53bd6308fc2f..e507bb285fd0 100644 --- a/drivers/dma/sh/shdma.c +++ b/drivers/dma/sh/shdma.c @@ -515,7 +515,8 @@ static int sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id, struct shdma_chan *schan; int err; - sh_chan = kzalloc(sizeof(struct sh_dmae_chan), GFP_KERNEL); + sh_chan = devm_kzalloc(sdev->dma_dev.dev, sizeof(struct sh_dmae_chan), + GFP_KERNEL); if (!sh_chan) { dev_err(sdev->dma_dev.dev, "No free memory for allocating dma channels!\n"); @@ -551,7 +552,6 @@ static int sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id, err_no_irq: /* remove from dmaengine device node */ shdma_chan_remove(schan); - kfree(sh_chan); return err; } @@ -562,14 +562,9 @@ static void sh_dmae_chan_remove(struct sh_dmae_device *shdev) int i; shdma_for_each_chan(schan, &shdev->shdma_dev, i) { - struct sh_dmae_chan *sh_chan = container_of(schan, - struct sh_dmae_chan, shdma_chan); BUG_ON(!schan); - shdma_free_irq(&sh_chan->shdma_chan); - shdma_chan_remove(schan); - kfree(sh_chan); } dma_dev->chancnt = 0; } @@ -706,33 +701,22 @@ static int sh_dmae_probe(struct platform_device *pdev) if (!chan || !errirq_res) return -ENODEV; - if (!request_mem_region(chan->start, resource_size(chan), pdev->name)) { - dev_err(&pdev->dev, "DMAC register region already claimed\n"); - return -EBUSY; - } - - if (dmars && !request_mem_region(dmars->start, resource_size(dmars), pdev->name)) { - dev_err(&pdev->dev, "DMAC DMARS region already claimed\n"); - err = -EBUSY; - goto ermrdmars; - } - - err = -ENOMEM; - shdev = kzalloc(sizeof(struct sh_dmae_device), GFP_KERNEL); + shdev = devm_kzalloc(&pdev->dev, sizeof(struct sh_dmae_device), + GFP_KERNEL); if (!shdev) { dev_err(&pdev->dev, "Not enough memory\n"); - goto ealloc; + return -ENOMEM; } dma_dev = &shdev->shdma_dev.dma_dev; - shdev->chan_reg = ioremap(chan->start, resource_size(chan)); - if (!shdev->chan_reg) - goto emapchan; + shdev->chan_reg = devm_ioremap_resource(&pdev->dev, chan); + if (IS_ERR(shdev->chan_reg)) + return PTR_ERR(shdev->chan_reg); if (dmars) { - shdev->dmars = ioremap(dmars->start, resource_size(dmars)); - if (!shdev->dmars) - goto emapdmars; + shdev->dmars = devm_ioremap_resource(&pdev->dev, dmars); + if (IS_ERR(shdev->dmars)) + return PTR_ERR(shdev->dmars); } if (!pdata->slave_only) @@ -793,8 +777,8 @@ static int sh_dmae_probe(struct platform_device *pdev) errirq = errirq_res->start; - err = request_irq(errirq, sh_dmae_err, irqflags, - "DMAC Address Error", shdev); + err = devm_request_irq(&pdev->dev, errirq, sh_dmae_err, irqflags, + "DMAC Address Error", shdev); if (err) { dev_err(&pdev->dev, "DMA failed requesting irq #%d, error %d\n", @@ -872,7 +856,6 @@ chan_probe_err: sh_dmae_chan_remove(shdev); #if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) - free_irq(errirq, shdev); eirq_err: #endif rst_err: @@ -886,18 +869,7 @@ rst_err: platform_set_drvdata(pdev, NULL); shdma_cleanup(&shdev->shdma_dev); eshdma: - if (dmars) - iounmap(shdev->dmars); -emapdmars: - iounmap(shdev->chan_reg); synchronize_rcu(); -emapchan: - kfree(shdev); -ealloc: - if (dmars) - release_mem_region(dmars->start, resource_size(dmars)); -ermrdmars: - release_mem_region(chan->start, resource_size(chan)); return err; } @@ -923,21 +895,9 @@ static int sh_dmae_remove(struct platform_device *pdev) sh_dmae_chan_remove(shdev); shdma_cleanup(&shdev->shdma_dev); - if (shdev->dmars) - iounmap(shdev->dmars); - iounmap(shdev->chan_reg); - platform_set_drvdata(pdev, NULL); synchronize_rcu(); - kfree(shdev); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) - release_mem_region(res->start, resource_size(res)); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (res) - release_mem_region(res->start, resource_size(res)); return 0; } diff --git a/drivers/dma/sh/sudmac.c b/drivers/dma/sh/sudmac.c index e7c94bbddb53..347790167e59 100644 --- a/drivers/dma/sh/sudmac.c +++ b/drivers/dma/sh/sudmac.c @@ -302,7 +302,6 @@ static void sudmac_chan_remove(struct sudmac_device *su_dev) BUG_ON(!schan); - shdma_free_irq(&sc->shdma_chan); shdma_chan_remove(schan); } dma_dev->chancnt = 0; diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index 5b1c9848124c..31cf89fb1d5b 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h @@ -116,7 +116,6 @@ struct shdma_dev { int shdma_request_irq(struct shdma_chan *, int, unsigned long, const char *); -void shdma_free_irq(struct shdma_chan *); bool shdma_reset(struct shdma_dev *sdev); void shdma_chan_probe(struct shdma_dev *sdev, struct shdma_chan *schan, int id); -- cgit v1.2.3-71-gd317 From 4981c4dc194efb18f0e9a02f1b43e926f2f0d2bb Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 2 Aug 2013 16:50:36 +0200 Subject: DMA: shdma: switch DT mode to use configuration data from a match table This facilitates DMAC DT support by eliminating the need in AUXDATA and avoiding creating complex DT data. This also fits well with DMAC devices, of which SoCs often have multiple identical copies and it is perfectly valid to use a single configuration data set for all of them. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/dma/shdma.txt | 61 +- drivers/dma/sh/Makefile | 2 + drivers/dma/sh/shdma-base.c | 15 +- drivers/dma/sh/shdma-of.c | 3 - drivers/dma/sh/shdma.c | 944 ----------------------- drivers/dma/sh/shdma.h | 1 + drivers/dma/sh/shdmac.c | 952 ++++++++++++++++++++++++ drivers/dma/sh/sudmac.c | 3 +- include/linux/shdma-base.h | 2 +- 9 files changed, 1002 insertions(+), 981 deletions(-) delete mode 100644 drivers/dma/sh/shdma.c create mode 100644 drivers/dma/sh/shdmac.c (limited to 'include') diff --git a/Documentation/devicetree/bindings/dma/shdma.txt b/Documentation/devicetree/bindings/dma/shdma.txt index c15994aa1939..2a3f3b8946b9 100644 --- a/Documentation/devicetree/bindings/dma/shdma.txt +++ b/Documentation/devicetree/bindings/dma/shdma.txt @@ -22,42 +22,51 @@ Optional properties (currently unused): * DMA controller Required properties: -- compatible: should be "renesas,shdma" +- compatible: should be of the form "renesas,shdma-", where should + be replaced with the desired SoC model, e.g. + "renesas,shdma-r8a73a4" for the system DMAC on r8a73a4 SoC Example: - dmac: dma-mux0 { + dmac: dma-multiplexer@0 { compatible = "renesas,shdma-mux"; #dma-cells = <1>; - dma-channels = <6>; + dma-channels = <20>; dma-requests = <256>; - reg = <0 0>; /* Needed for AUXDATA */ - #address-cells = <1>; - #size-cells = <1>; + #address-cells = <2>; + #size-cells = <2>; ranges; - dma0: shdma@fe008020 { - compatible = "renesas,shdma"; - reg = <0xfe008020 0x270>, - <0xfe009000 0xc>; + dma0: dma-controller@e6700020 { + compatible = "renesas,shdma-r8a73a4"; + reg = <0 0xe6700020 0 0x89e0>; interrupt-parent = <&gic>; - interrupts = <0 34 4 - 0 28 4 - 0 29 4 - 0 30 4 - 0 31 4 - 0 32 4 - 0 33 4>; + interrupts = <0 220 4 + 0 200 4 + 0 201 4 + 0 202 4 + 0 203 4 + 0 204 4 + 0 205 4 + 0 206 4 + 0 207 4 + 0 208 4 + 0 209 4 + 0 210 4 + 0 211 4 + 0 212 4 + 0 213 4 + 0 214 4 + 0 215 4 + 0 216 4 + 0 217 4 + 0 218 4 + 0 219 4>; interrupt-names = "error", "ch0", "ch1", "ch2", "ch3", - "ch4", "ch5"; - }; - - dma1: shdma@fe018020 { - ... - }; - - dma2: shdma@fe028020 { - ... + "ch4", "ch5", "ch6", "ch7", + "ch8", "ch9", "ch10", "ch11", + "ch12", "ch13", "ch14", "ch15", + "ch16", "ch17", "ch18", "ch19"; }; }; diff --git a/drivers/dma/sh/Makefile b/drivers/dma/sh/Makefile index c962138dde96..893ee095f93c 100644 --- a/drivers/dma/sh/Makefile +++ b/drivers/dma/sh/Makefile @@ -1,3 +1,5 @@ obj-$(CONFIG_SH_DMAE_BASE) += shdma-base.o shdma-of.o obj-$(CONFIG_SH_DMAE) += shdma.o +shdma-y := shdmac.o +shdma-objs := $(shdma-y) obj-$(CONFIG_SUDMAC) += sudmac.o diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index c5ea256c2819..d94ab592cc1b 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -171,7 +171,8 @@ static struct shdma_desc *shdma_get_desc(struct shdma_chan *schan) return NULL; } -static int shdma_setup_slave(struct shdma_chan *schan, int slave_id) +static int shdma_setup_slave(struct shdma_chan *schan, int slave_id, + dma_addr_t slave_addr) { struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device); const struct shdma_ops *ops = sdev->ops; @@ -179,7 +180,7 @@ static int shdma_setup_slave(struct shdma_chan *schan, int slave_id) if (schan->dev->of_node) { match = schan->hw_req; - ret = ops->set_slave(schan, match, true); + ret = ops->set_slave(schan, match, slave_addr, true); if (ret < 0) return ret; @@ -194,7 +195,7 @@ static int shdma_setup_slave(struct shdma_chan *schan, int slave_id) if (test_and_set_bit(slave_id, shdma_slave_used)) return -EBUSY; - ret = ops->set_slave(schan, match, false); + ret = ops->set_slave(schan, match, slave_addr, false); if (ret < 0) { clear_bit(slave_id, shdma_slave_used); return ret; @@ -236,7 +237,7 @@ bool shdma_chan_filter(struct dma_chan *chan, void *arg) if (!schan->dev->of_node && match >= slave_num) return false; - ret = ops->set_slave(schan, match, true); + ret = ops->set_slave(schan, match, 0, true); if (ret < 0) return false; @@ -259,7 +260,7 @@ static int shdma_alloc_chan_resources(struct dma_chan *chan) */ if (slave) { /* Legacy mode: .private is set in filter */ - ret = shdma_setup_slave(schan, slave->slave_id); + ret = shdma_setup_slave(schan, slave->slave_id, 0); if (ret < 0) goto esetslave; } else { @@ -680,7 +681,9 @@ static int shdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, * channel, while using it... */ config = (struct dma_slave_config *)arg; - ret = shdma_setup_slave(schan, config->slave_id); + ret = shdma_setup_slave(schan, config->slave_id, + config->direction == DMA_DEV_TO_MEM ? + config->src_addr : config->dst_addr); if (ret < 0) return ret; break; diff --git a/drivers/dma/sh/shdma-of.c b/drivers/dma/sh/shdma-of.c index 11bcb05cd79c..2acf7b62d6b6 100644 --- a/drivers/dma/sh/shdma-of.c +++ b/drivers/dma/sh/shdma-of.c @@ -45,9 +45,6 @@ static int shdma_of_probe(struct platform_device *pdev) const struct of_dev_auxdata *lookup = pdev->dev.platform_data; int ret; - if (!lookup) - return -EINVAL; - ret = of_dma_controller_register(pdev->dev.of_node, shdma_of_xlate, pdev); if (ret < 0) diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c deleted file mode 100644 index 3b4bee933dd7..000000000000 --- a/drivers/dma/sh/shdma.c +++ /dev/null @@ -1,944 +0,0 @@ -/* - * Renesas SuperH DMA Engine support - * - * base is drivers/dma/flsdma.c - * - * Copyright (C) 2011-2012 Guennadi Liakhovetski - * Copyright (C) 2009 Nobuhiro Iwamatsu - * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved. - * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. - * - * This 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; either version 2 of the License, or - * (at your option) any later version. - * - * - DMA of SuperH does not have Hardware DMA chain mode. - * - MAX DMA size is 16MB. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../dmaengine.h" -#include "shdma.h" - -#define SH_DMAE_DRV_NAME "sh-dma-engine" - -/* Default MEMCPY transfer size = 2^2 = 4 bytes */ -#define LOG2_DEFAULT_XFER_SIZE 2 -#define SH_DMA_SLAVE_NUMBER 256 -#define SH_DMA_TCR_MAX (16 * 1024 * 1024 - 1) - -/* - * Used for write-side mutual exclusion for the global device list, - * read-side synchronization by way of RCU, and per-controller data. - */ -static DEFINE_SPINLOCK(sh_dmae_lock); -static LIST_HEAD(sh_dmae_devices); - -/* - * Different DMAC implementations provide different ways to clear DMA channels: - * (1) none - no CHCLR registers are available - * (2) one CHCLR register per channel - 0 has to be written to it to clear - * channel buffers - * (3) one CHCLR per several channels - 1 has to be written to the bit, - * corresponding to the specific channel to reset it - */ -static void channel_clear(struct sh_dmae_chan *sh_dc) -{ - struct sh_dmae_device *shdev = to_sh_dev(sh_dc); - const struct sh_dmae_channel *chan_pdata = shdev->pdata->channel + - sh_dc->shdma_chan.id; - u32 val = shdev->pdata->chclr_bitwise ? 1 << chan_pdata->chclr_bit : 0; - - __raw_writel(val, shdev->chan_reg + chan_pdata->chclr_offset); -} - -static void sh_dmae_writel(struct sh_dmae_chan *sh_dc, u32 data, u32 reg) -{ - __raw_writel(data, sh_dc->base + reg); -} - -static u32 sh_dmae_readl(struct sh_dmae_chan *sh_dc, u32 reg) -{ - return __raw_readl(sh_dc->base + reg); -} - -static u16 dmaor_read(struct sh_dmae_device *shdev) -{ - void __iomem *addr = shdev->chan_reg + DMAOR; - - if (shdev->pdata->dmaor_is_32bit) - return __raw_readl(addr); - else - return __raw_readw(addr); -} - -static void dmaor_write(struct sh_dmae_device *shdev, u16 data) -{ - void __iomem *addr = shdev->chan_reg + DMAOR; - - if (shdev->pdata->dmaor_is_32bit) - __raw_writel(data, addr); - else - __raw_writew(data, addr); -} - -static void chcr_write(struct sh_dmae_chan *sh_dc, u32 data) -{ - struct sh_dmae_device *shdev = to_sh_dev(sh_dc); - - __raw_writel(data, sh_dc->base + shdev->chcr_offset); -} - -static u32 chcr_read(struct sh_dmae_chan *sh_dc) -{ - struct sh_dmae_device *shdev = to_sh_dev(sh_dc); - - return __raw_readl(sh_dc->base + shdev->chcr_offset); -} - -/* - * Reset DMA controller - * - * SH7780 has two DMAOR register - */ -static void sh_dmae_ctl_stop(struct sh_dmae_device *shdev) -{ - unsigned short dmaor; - unsigned long flags; - - spin_lock_irqsave(&sh_dmae_lock, flags); - - dmaor = dmaor_read(shdev); - dmaor_write(shdev, dmaor & ~(DMAOR_NMIF | DMAOR_AE | DMAOR_DME)); - - spin_unlock_irqrestore(&sh_dmae_lock, flags); -} - -static int sh_dmae_rst(struct sh_dmae_device *shdev) -{ - unsigned short dmaor; - unsigned long flags; - - spin_lock_irqsave(&sh_dmae_lock, flags); - - dmaor = dmaor_read(shdev) & ~(DMAOR_NMIF | DMAOR_AE | DMAOR_DME); - - if (shdev->pdata->chclr_present) { - int i; - for (i = 0; i < shdev->pdata->channel_num; i++) { - struct sh_dmae_chan *sh_chan = shdev->chan[i]; - if (sh_chan) - channel_clear(sh_chan); - } - } - - dmaor_write(shdev, dmaor | shdev->pdata->dmaor_init); - - dmaor = dmaor_read(shdev); - - spin_unlock_irqrestore(&sh_dmae_lock, flags); - - if (dmaor & (DMAOR_AE | DMAOR_NMIF)) { - dev_warn(shdev->shdma_dev.dma_dev.dev, "Can't initialize DMAOR.\n"); - return -EIO; - } - if (shdev->pdata->dmaor_init & ~dmaor) - dev_warn(shdev->shdma_dev.dma_dev.dev, - "DMAOR=0x%x hasn't latched the initial value 0x%x.\n", - dmaor, shdev->pdata->dmaor_init); - return 0; -} - -static bool dmae_is_busy(struct sh_dmae_chan *sh_chan) -{ - u32 chcr = chcr_read(sh_chan); - - if ((chcr & (CHCR_DE | CHCR_TE)) == CHCR_DE) - return true; /* working */ - - return false; /* waiting */ -} - -static unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan, u32 chcr) -{ - struct sh_dmae_device *shdev = to_sh_dev(sh_chan); - const struct sh_dmae_pdata *pdata = shdev->pdata; - int cnt = ((chcr & pdata->ts_low_mask) >> pdata->ts_low_shift) | - ((chcr & pdata->ts_high_mask) >> pdata->ts_high_shift); - - if (cnt >= pdata->ts_shift_num) - cnt = 0; - - return pdata->ts_shift[cnt]; -} - -static u32 log2size_to_chcr(struct sh_dmae_chan *sh_chan, int l2size) -{ - struct sh_dmae_device *shdev = to_sh_dev(sh_chan); - const struct sh_dmae_pdata *pdata = shdev->pdata; - int i; - - for (i = 0; i < pdata->ts_shift_num; i++) - if (pdata->ts_shift[i] == l2size) - break; - - if (i == pdata->ts_shift_num) - i = 0; - - return ((i << pdata->ts_low_shift) & pdata->ts_low_mask) | - ((i << pdata->ts_high_shift) & pdata->ts_high_mask); -} - -static void dmae_set_reg(struct sh_dmae_chan *sh_chan, struct sh_dmae_regs *hw) -{ - sh_dmae_writel(sh_chan, hw->sar, SAR); - sh_dmae_writel(sh_chan, hw->dar, DAR); - sh_dmae_writel(sh_chan, hw->tcr >> sh_chan->xmit_shift, TCR); -} - -static void dmae_start(struct sh_dmae_chan *sh_chan) -{ - struct sh_dmae_device *shdev = to_sh_dev(sh_chan); - u32 chcr = chcr_read(sh_chan); - - if (shdev->pdata->needs_tend_set) - sh_dmae_writel(sh_chan, 0xFFFFFFFF, TEND); - - chcr |= CHCR_DE | shdev->chcr_ie_bit; - chcr_write(sh_chan, chcr & ~CHCR_TE); -} - -static void dmae_init(struct sh_dmae_chan *sh_chan) -{ - /* - * Default configuration for dual address memory-memory transfer. - * 0x400 represents auto-request. - */ - u32 chcr = DM_INC | SM_INC | 0x400 | log2size_to_chcr(sh_chan, - LOG2_DEFAULT_XFER_SIZE); - sh_chan->xmit_shift = calc_xmit_shift(sh_chan, chcr); - chcr_write(sh_chan, chcr); -} - -static int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val) -{ - /* If DMA is active, cannot set CHCR. TODO: remove this superfluous check */ - if (dmae_is_busy(sh_chan)) - return -EBUSY; - - sh_chan->xmit_shift = calc_xmit_shift(sh_chan, val); - chcr_write(sh_chan, val); - - return 0; -} - -static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val) -{ - struct sh_dmae_device *shdev = to_sh_dev(sh_chan); - const struct sh_dmae_pdata *pdata = shdev->pdata; - const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->shdma_chan.id]; - void __iomem *addr = shdev->dmars; - unsigned int shift = chan_pdata->dmars_bit; - - if (dmae_is_busy(sh_chan)) - return -EBUSY; - - if (pdata->no_dmars) - return 0; - - /* in the case of a missing DMARS resource use first memory window */ - if (!addr) - addr = shdev->chan_reg; - addr += chan_pdata->dmars; - - __raw_writew((__raw_readw(addr) & (0xff00 >> shift)) | (val << shift), - addr); - - return 0; -} - -static void sh_dmae_start_xfer(struct shdma_chan *schan, - struct shdma_desc *sdesc) -{ - struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, - shdma_chan); - struct sh_dmae_desc *sh_desc = container_of(sdesc, - struct sh_dmae_desc, shdma_desc); - dev_dbg(sh_chan->shdma_chan.dev, "Queue #%d to %d: %u@%x -> %x\n", - sdesc->async_tx.cookie, sh_chan->shdma_chan.id, - sh_desc->hw.tcr, sh_desc->hw.sar, sh_desc->hw.dar); - /* Get the ld start address from ld_queue */ - dmae_set_reg(sh_chan, &sh_desc->hw); - dmae_start(sh_chan); -} - -static bool sh_dmae_channel_busy(struct shdma_chan *schan) -{ - struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, - shdma_chan); - return dmae_is_busy(sh_chan); -} - -static void sh_dmae_setup_xfer(struct shdma_chan *schan, - int slave_id) -{ - struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, - shdma_chan); - - if (slave_id >= 0) { - const struct sh_dmae_slave_config *cfg = - sh_chan->config; - - dmae_set_dmars(sh_chan, cfg->mid_rid); - dmae_set_chcr(sh_chan, cfg->chcr); - } else { - dmae_init(sh_chan); - } -} - -/* - * Find a slave channel configuration from the contoller list by either a slave - * ID in the non-DT case, or by a MID/RID value in the DT case - */ -static const struct sh_dmae_slave_config *dmae_find_slave( - struct sh_dmae_chan *sh_chan, int match) -{ - struct sh_dmae_device *shdev = to_sh_dev(sh_chan); - const struct sh_dmae_pdata *pdata = shdev->pdata; - const struct sh_dmae_slave_config *cfg; - int i; - - if (!sh_chan->shdma_chan.dev->of_node) { - if (match >= SH_DMA_SLAVE_NUMBER) - return NULL; - - for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++) - if (cfg->slave_id == match) - return cfg; - } else { - for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++) - if (cfg->mid_rid == match) { - sh_chan->shdma_chan.slave_id = cfg->slave_id; - return cfg; - } - } - - return NULL; -} - -static int sh_dmae_set_slave(struct shdma_chan *schan, - int slave_id, bool try) -{ - struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, - shdma_chan); - const struct sh_dmae_slave_config *cfg = dmae_find_slave(sh_chan, slave_id); - if (!cfg) - return -ENXIO; - - if (!try) - sh_chan->config = cfg; - - return 0; -} - -static void dmae_halt(struct sh_dmae_chan *sh_chan) -{ - struct sh_dmae_device *shdev = to_sh_dev(sh_chan); - u32 chcr = chcr_read(sh_chan); - - chcr &= ~(CHCR_DE | CHCR_TE | shdev->chcr_ie_bit); - chcr_write(sh_chan, chcr); -} - -static int sh_dmae_desc_setup(struct shdma_chan *schan, - struct shdma_desc *sdesc, - dma_addr_t src, dma_addr_t dst, size_t *len) -{ - struct sh_dmae_desc *sh_desc = container_of(sdesc, - struct sh_dmae_desc, shdma_desc); - - if (*len > schan->max_xfer_len) - *len = schan->max_xfer_len; - - sh_desc->hw.sar = src; - sh_desc->hw.dar = dst; - sh_desc->hw.tcr = *len; - - return 0; -} - -static void sh_dmae_halt(struct shdma_chan *schan) -{ - struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, - shdma_chan); - dmae_halt(sh_chan); -} - -static bool sh_dmae_chan_irq(struct shdma_chan *schan, int irq) -{ - struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, - shdma_chan); - - if (!(chcr_read(sh_chan) & CHCR_TE)) - return false; - - /* DMA stop */ - dmae_halt(sh_chan); - - return true; -} - -static size_t sh_dmae_get_partial(struct shdma_chan *schan, - struct shdma_desc *sdesc) -{ - struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, - shdma_chan); - struct sh_dmae_desc *sh_desc = container_of(sdesc, - struct sh_dmae_desc, shdma_desc); - return (sh_desc->hw.tcr - sh_dmae_readl(sh_chan, TCR)) << - sh_chan->xmit_shift; -} - -/* Called from error IRQ or NMI */ -static bool sh_dmae_reset(struct sh_dmae_device *shdev) -{ - bool ret; - - /* halt the dma controller */ - sh_dmae_ctl_stop(shdev); - - /* We cannot detect, which channel caused the error, have to reset all */ - ret = shdma_reset(&shdev->shdma_dev); - - sh_dmae_rst(shdev); - - return ret; -} - -static irqreturn_t sh_dmae_err(int irq, void *data) -{ - struct sh_dmae_device *shdev = data; - - if (!(dmaor_read(shdev) & DMAOR_AE)) - return IRQ_NONE; - - sh_dmae_reset(shdev); - return IRQ_HANDLED; -} - -static bool sh_dmae_desc_completed(struct shdma_chan *schan, - struct shdma_desc *sdesc) -{ - struct sh_dmae_chan *sh_chan = container_of(schan, - struct sh_dmae_chan, shdma_chan); - struct sh_dmae_desc *sh_desc = container_of(sdesc, - struct sh_dmae_desc, shdma_desc); - u32 sar_buf = sh_dmae_readl(sh_chan, SAR); - u32 dar_buf = sh_dmae_readl(sh_chan, DAR); - - return (sdesc->direction == DMA_DEV_TO_MEM && - (sh_desc->hw.dar + sh_desc->hw.tcr) == dar_buf) || - (sdesc->direction != DMA_DEV_TO_MEM && - (sh_desc->hw.sar + sh_desc->hw.tcr) == sar_buf); -} - -static bool sh_dmae_nmi_notify(struct sh_dmae_device *shdev) -{ - /* Fast path out if NMIF is not asserted for this controller */ - if ((dmaor_read(shdev) & DMAOR_NMIF) == 0) - return false; - - return sh_dmae_reset(shdev); -} - -static int sh_dmae_nmi_handler(struct notifier_block *self, - unsigned long cmd, void *data) -{ - struct sh_dmae_device *shdev; - int ret = NOTIFY_DONE; - bool triggered; - - /* - * Only concern ourselves with NMI events. - * - * Normally we would check the die chain value, but as this needs - * to be architecture independent, check for NMI context instead. - */ - if (!in_nmi()) - return NOTIFY_DONE; - - rcu_read_lock(); - list_for_each_entry_rcu(shdev, &sh_dmae_devices, node) { - /* - * Only stop if one of the controllers has NMIF asserted, - * we do not want to interfere with regular address error - * handling or NMI events that don't concern the DMACs. - */ - triggered = sh_dmae_nmi_notify(shdev); - if (triggered == true) - ret = NOTIFY_OK; - } - rcu_read_unlock(); - - return ret; -} - -static struct notifier_block sh_dmae_nmi_notifier __read_mostly = { - .notifier_call = sh_dmae_nmi_handler, - - /* Run before NMI debug handler and KGDB */ - .priority = 1, -}; - -static int sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id, - int irq, unsigned long flags) -{ - const struct sh_dmae_channel *chan_pdata = &shdev->pdata->channel[id]; - struct shdma_dev *sdev = &shdev->shdma_dev; - struct platform_device *pdev = to_platform_device(sdev->dma_dev.dev); - struct sh_dmae_chan *sh_chan; - struct shdma_chan *schan; - int err; - - sh_chan = devm_kzalloc(sdev->dma_dev.dev, sizeof(struct sh_dmae_chan), - GFP_KERNEL); - if (!sh_chan) { - dev_err(sdev->dma_dev.dev, - "No free memory for allocating dma channels!\n"); - return -ENOMEM; - } - - schan = &sh_chan->shdma_chan; - schan->max_xfer_len = SH_DMA_TCR_MAX + 1; - - shdma_chan_probe(sdev, schan, id); - - sh_chan->base = shdev->chan_reg + chan_pdata->offset; - - /* set up channel irq */ - if (pdev->id >= 0) - snprintf(sh_chan->dev_id, sizeof(sh_chan->dev_id), - "sh-dmae%d.%d", pdev->id, id); - else - snprintf(sh_chan->dev_id, sizeof(sh_chan->dev_id), - "sh-dma%d", id); - - err = shdma_request_irq(schan, irq, flags, sh_chan->dev_id); - if (err) { - dev_err(sdev->dma_dev.dev, - "DMA channel %d request_irq error %d\n", - id, err); - goto err_no_irq; - } - - shdev->chan[id] = sh_chan; - return 0; - -err_no_irq: - /* remove from dmaengine device node */ - shdma_chan_remove(schan); - return err; -} - -static void sh_dmae_chan_remove(struct sh_dmae_device *shdev) -{ - struct dma_device *dma_dev = &shdev->shdma_dev.dma_dev; - struct shdma_chan *schan; - int i; - - shdma_for_each_chan(schan, &shdev->shdma_dev, i) { - BUG_ON(!schan); - - shdma_chan_remove(schan); - } - dma_dev->chancnt = 0; -} - -static void sh_dmae_shutdown(struct platform_device *pdev) -{ - struct sh_dmae_device *shdev = platform_get_drvdata(pdev); - sh_dmae_ctl_stop(shdev); -} - -static int sh_dmae_runtime_suspend(struct device *dev) -{ - return 0; -} - -static int sh_dmae_runtime_resume(struct device *dev) -{ - struct sh_dmae_device *shdev = dev_get_drvdata(dev); - - return sh_dmae_rst(shdev); -} - -#ifdef CONFIG_PM -static int sh_dmae_suspend(struct device *dev) -{ - return 0; -} - -static int sh_dmae_resume(struct device *dev) -{ - struct sh_dmae_device *shdev = dev_get_drvdata(dev); - int i, ret; - - ret = sh_dmae_rst(shdev); - if (ret < 0) - dev_err(dev, "Failed to reset!\n"); - - for (i = 0; i < shdev->pdata->channel_num; i++) { - struct sh_dmae_chan *sh_chan = shdev->chan[i]; - - if (!sh_chan->shdma_chan.desc_num) - continue; - - if (sh_chan->shdma_chan.slave_id >= 0) { - const struct sh_dmae_slave_config *cfg = sh_chan->config; - dmae_set_dmars(sh_chan, cfg->mid_rid); - dmae_set_chcr(sh_chan, cfg->chcr); - } else { - dmae_init(sh_chan); - } - } - - return 0; -} -#else -#define sh_dmae_suspend NULL -#define sh_dmae_resume NULL -#endif - -const struct dev_pm_ops sh_dmae_pm = { - .suspend = sh_dmae_suspend, - .resume = sh_dmae_resume, - .runtime_suspend = sh_dmae_runtime_suspend, - .runtime_resume = sh_dmae_runtime_resume, -}; - -static dma_addr_t sh_dmae_slave_addr(struct shdma_chan *schan) -{ - struct sh_dmae_chan *sh_chan = container_of(schan, - struct sh_dmae_chan, shdma_chan); - - /* - * Implicit BUG_ON(!sh_chan->config) - * This is an exclusive slave DMA operation, may only be called after a - * successful slave configuration. - */ - return sh_chan->config->addr; -} - -static struct shdma_desc *sh_dmae_embedded_desc(void *buf, int i) -{ - return &((struct sh_dmae_desc *)buf)[i].shdma_desc; -} - -static const struct shdma_ops sh_dmae_shdma_ops = { - .desc_completed = sh_dmae_desc_completed, - .halt_channel = sh_dmae_halt, - .channel_busy = sh_dmae_channel_busy, - .slave_addr = sh_dmae_slave_addr, - .desc_setup = sh_dmae_desc_setup, - .set_slave = sh_dmae_set_slave, - .setup_xfer = sh_dmae_setup_xfer, - .start_xfer = sh_dmae_start_xfer, - .embedded_desc = sh_dmae_embedded_desc, - .chan_irq = sh_dmae_chan_irq, - .get_partial = sh_dmae_get_partial, -}; - -static int sh_dmae_probe(struct platform_device *pdev) -{ - const struct sh_dmae_pdata *pdata = pdev->dev.platform_data; - unsigned long irqflags = IRQF_DISABLED, - chan_flag[SH_DMAE_MAX_CHANNELS] = {}; - int errirq, chan_irq[SH_DMAE_MAX_CHANNELS]; - int err, i, irq_cnt = 0, irqres = 0, irq_cap = 0; - struct sh_dmae_device *shdev; - struct dma_device *dma_dev; - struct resource *chan, *dmars, *errirq_res, *chanirq_res; - - /* get platform data */ - if (!pdata || !pdata->channel_num) - return -ENODEV; - - chan = platform_get_resource(pdev, IORESOURCE_MEM, 0); - /* DMARS area is optional */ - dmars = platform_get_resource(pdev, IORESOURCE_MEM, 1); - /* - * IRQ resources: - * 1. there always must be at least one IRQ IO-resource. On SH4 it is - * the error IRQ, in which case it is the only IRQ in this resource: - * start == end. If it is the only IRQ resource, all channels also - * use the same IRQ. - * 2. DMA channel IRQ resources can be specified one per resource or in - * ranges (start != end) - * 3. iff all events (channels and, optionally, error) on this - * controller use the same IRQ, only one IRQ resource can be - * specified, otherwise there must be one IRQ per channel, even if - * some of them are equal - * 4. if all IRQs on this controller are equal or if some specific IRQs - * specify IORESOURCE_IRQ_SHAREABLE in their resources, they will be - * requested with the IRQF_SHARED flag - */ - errirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!chan || !errirq_res) - return -ENODEV; - - shdev = devm_kzalloc(&pdev->dev, sizeof(struct sh_dmae_device), - GFP_KERNEL); - if (!shdev) { - dev_err(&pdev->dev, "Not enough memory\n"); - return -ENOMEM; - } - - dma_dev = &shdev->shdma_dev.dma_dev; - - shdev->chan_reg = devm_ioremap_resource(&pdev->dev, chan); - if (IS_ERR(shdev->chan_reg)) - return PTR_ERR(shdev->chan_reg); - if (dmars) { - shdev->dmars = devm_ioremap_resource(&pdev->dev, dmars); - if (IS_ERR(shdev->dmars)) - return PTR_ERR(shdev->dmars); - } - - if (!pdata->slave_only) - dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask); - if (pdata->slave && pdata->slave_num) - dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); - - /* Default transfer size of 32 bytes requires 32-byte alignment */ - dma_dev->copy_align = LOG2_DEFAULT_XFER_SIZE; - - shdev->shdma_dev.ops = &sh_dmae_shdma_ops; - shdev->shdma_dev.desc_size = sizeof(struct sh_dmae_desc); - err = shdma_init(&pdev->dev, &shdev->shdma_dev, - pdata->channel_num); - if (err < 0) - goto eshdma; - - /* platform data */ - shdev->pdata = pdata; - - if (pdata->chcr_offset) - shdev->chcr_offset = pdata->chcr_offset; - else - shdev->chcr_offset = CHCR; - - if (pdata->chcr_ie_bit) - shdev->chcr_ie_bit = pdata->chcr_ie_bit; - else - shdev->chcr_ie_bit = CHCR_IE; - - platform_set_drvdata(pdev, shdev); - - pm_runtime_enable(&pdev->dev); - err = pm_runtime_get_sync(&pdev->dev); - if (err < 0) - dev_err(&pdev->dev, "%s(): GET = %d\n", __func__, err); - - spin_lock_irq(&sh_dmae_lock); - list_add_tail_rcu(&shdev->node, &sh_dmae_devices); - spin_unlock_irq(&sh_dmae_lock); - - /* reset dma controller - only needed as a test */ - err = sh_dmae_rst(shdev); - if (err) - goto rst_err; - -#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) - chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); - - if (!chanirq_res) - chanirq_res = errirq_res; - else - irqres++; - - if (chanirq_res == errirq_res || - (errirq_res->flags & IORESOURCE_BITS) == IORESOURCE_IRQ_SHAREABLE) - irqflags = IRQF_SHARED; - - errirq = errirq_res->start; - - err = devm_request_irq(&pdev->dev, errirq, sh_dmae_err, irqflags, - "DMAC Address Error", shdev); - if (err) { - dev_err(&pdev->dev, - "DMA failed requesting irq #%d, error %d\n", - errirq, err); - goto eirq_err; - } - -#else - chanirq_res = errirq_res; -#endif /* CONFIG_CPU_SH4 || CONFIG_ARCH_SHMOBILE */ - - if (chanirq_res->start == chanirq_res->end && - !platform_get_resource(pdev, IORESOURCE_IRQ, 1)) { - /* Special case - all multiplexed */ - for (; irq_cnt < pdata->channel_num; irq_cnt++) { - if (irq_cnt < SH_DMAE_MAX_CHANNELS) { - chan_irq[irq_cnt] = chanirq_res->start; - chan_flag[irq_cnt] = IRQF_SHARED; - } else { - irq_cap = 1; - break; - } - } - } else { - do { - for (i = chanirq_res->start; i <= chanirq_res->end; i++) { - if (irq_cnt >= SH_DMAE_MAX_CHANNELS) { - irq_cap = 1; - break; - } - - if ((errirq_res->flags & IORESOURCE_BITS) == - IORESOURCE_IRQ_SHAREABLE) - chan_flag[irq_cnt] = IRQF_SHARED; - else - chan_flag[irq_cnt] = IRQF_DISABLED; - dev_dbg(&pdev->dev, - "Found IRQ %d for channel %d\n", - i, irq_cnt); - chan_irq[irq_cnt++] = i; - } - - if (irq_cnt >= SH_DMAE_MAX_CHANNELS) - break; - - chanirq_res = platform_get_resource(pdev, - IORESOURCE_IRQ, ++irqres); - } while (irq_cnt < pdata->channel_num && chanirq_res); - } - - /* Create DMA Channel */ - for (i = 0; i < irq_cnt; i++) { - err = sh_dmae_chan_probe(shdev, i, chan_irq[i], chan_flag[i]); - if (err) - goto chan_probe_err; - } - - if (irq_cap) - dev_notice(&pdev->dev, "Attempting to register %d DMA " - "channels when a maximum of %d are supported.\n", - pdata->channel_num, SH_DMAE_MAX_CHANNELS); - - pm_runtime_put(&pdev->dev); - - err = dma_async_device_register(&shdev->shdma_dev.dma_dev); - if (err < 0) - goto edmadevreg; - - return err; - -edmadevreg: - pm_runtime_get(&pdev->dev); - -chan_probe_err: - sh_dmae_chan_remove(shdev); - -#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) -eirq_err: -#endif -rst_err: - spin_lock_irq(&sh_dmae_lock); - list_del_rcu(&shdev->node); - spin_unlock_irq(&sh_dmae_lock); - - pm_runtime_put(&pdev->dev); - pm_runtime_disable(&pdev->dev); - - platform_set_drvdata(pdev, NULL); - shdma_cleanup(&shdev->shdma_dev); -eshdma: - synchronize_rcu(); - - return err; -} - -static int sh_dmae_remove(struct platform_device *pdev) -{ - struct sh_dmae_device *shdev = platform_get_drvdata(pdev); - struct dma_device *dma_dev = &shdev->shdma_dev.dma_dev; - struct resource *res; - int errirq = platform_get_irq(pdev, 0); - - dma_async_device_unregister(dma_dev); - - if (errirq > 0) - free_irq(errirq, shdev); - - spin_lock_irq(&sh_dmae_lock); - list_del_rcu(&shdev->node); - spin_unlock_irq(&sh_dmae_lock); - - pm_runtime_disable(&pdev->dev); - - sh_dmae_chan_remove(shdev); - shdma_cleanup(&shdev->shdma_dev); - - platform_set_drvdata(pdev, NULL); - - synchronize_rcu(); - - return 0; -} - -static const struct of_device_id sh_dmae_of_match[] = { - { .compatible = "renesas,shdma", }, - { } -}; -MODULE_DEVICE_TABLE(of, sh_dmae_of_match); - -static struct platform_driver sh_dmae_driver = { - .driver = { - .owner = THIS_MODULE, - .pm = &sh_dmae_pm, - .name = SH_DMAE_DRV_NAME, - .of_match_table = sh_dmae_of_match, - }, - .remove = sh_dmae_remove, - .shutdown = sh_dmae_shutdown, -}; - -static int __init sh_dmae_init(void) -{ - /* Wire up NMI handling */ - int err = register_die_notifier(&sh_dmae_nmi_notifier); - if (err) - return err; - - return platform_driver_probe(&sh_dmae_driver, sh_dmae_probe); -} -module_init(sh_dmae_init); - -static void __exit sh_dmae_exit(void) -{ - platform_driver_unregister(&sh_dmae_driver); - - unregister_die_notifier(&sh_dmae_nmi_notifier); -} -module_exit(sh_dmae_exit); - -MODULE_AUTHOR("Nobuhiro Iwamatsu "); -MODULE_DESCRIPTION("Renesas SH DMA Engine driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" SH_DMAE_DRV_NAME); diff --git a/drivers/dma/sh/shdma.h b/drivers/dma/sh/shdma.h index 3d9dca177860..ff2f93b612ca 100644 --- a/drivers/dma/sh/shdma.h +++ b/drivers/dma/sh/shdma.h @@ -31,6 +31,7 @@ struct sh_dmae_chan { void __iomem *base; char dev_id[16]; /* unique name per DMAC of channel */ int pm_error; + dma_addr_t slave_addr; }; struct sh_dmae_device { diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c new file mode 100644 index 000000000000..a47b70879e05 --- /dev/null +++ b/drivers/dma/sh/shdmac.c @@ -0,0 +1,952 @@ +/* + * Renesas SuperH DMA Engine support + * + * base is drivers/dma/flsdma.c + * + * Copyright (C) 2011-2012 Guennadi Liakhovetski + * Copyright (C) 2009 Nobuhiro Iwamatsu + * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved. + * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. + * + * This 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; either version 2 of the License, or + * (at your option) any later version. + * + * - DMA of SuperH does not have Hardware DMA chain mode. + * - MAX DMA size is 16MB. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../dmaengine.h" +#include "shdma.h" + +#define SH_DMAE_DRV_NAME "sh-dma-engine" + +/* Default MEMCPY transfer size = 2^2 = 4 bytes */ +#define LOG2_DEFAULT_XFER_SIZE 2 +#define SH_DMA_SLAVE_NUMBER 256 +#define SH_DMA_TCR_MAX (16 * 1024 * 1024 - 1) + +/* + * Used for write-side mutual exclusion for the global device list, + * read-side synchronization by way of RCU, and per-controller data. + */ +static DEFINE_SPINLOCK(sh_dmae_lock); +static LIST_HEAD(sh_dmae_devices); + +/* + * Different DMAC implementations provide different ways to clear DMA channels: + * (1) none - no CHCLR registers are available + * (2) one CHCLR register per channel - 0 has to be written to it to clear + * channel buffers + * (3) one CHCLR per several channels - 1 has to be written to the bit, + * corresponding to the specific channel to reset it + */ +static void channel_clear(struct sh_dmae_chan *sh_dc) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_dc); + const struct sh_dmae_channel *chan_pdata = shdev->pdata->channel + + sh_dc->shdma_chan.id; + u32 val = shdev->pdata->chclr_bitwise ? 1 << chan_pdata->chclr_bit : 0; + + __raw_writel(val, shdev->chan_reg + chan_pdata->chclr_offset); +} + +static void sh_dmae_writel(struct sh_dmae_chan *sh_dc, u32 data, u32 reg) +{ + __raw_writel(data, sh_dc->base + reg); +} + +static u32 sh_dmae_readl(struct sh_dmae_chan *sh_dc, u32 reg) +{ + return __raw_readl(sh_dc->base + reg); +} + +static u16 dmaor_read(struct sh_dmae_device *shdev) +{ + void __iomem *addr = shdev->chan_reg + DMAOR; + + if (shdev->pdata->dmaor_is_32bit) + return __raw_readl(addr); + else + return __raw_readw(addr); +} + +static void dmaor_write(struct sh_dmae_device *shdev, u16 data) +{ + void __iomem *addr = shdev->chan_reg + DMAOR; + + if (shdev->pdata->dmaor_is_32bit) + __raw_writel(data, addr); + else + __raw_writew(data, addr); +} + +static void chcr_write(struct sh_dmae_chan *sh_dc, u32 data) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_dc); + + __raw_writel(data, sh_dc->base + shdev->chcr_offset); +} + +static u32 chcr_read(struct sh_dmae_chan *sh_dc) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_dc); + + return __raw_readl(sh_dc->base + shdev->chcr_offset); +} + +/* + * Reset DMA controller + * + * SH7780 has two DMAOR register + */ +static void sh_dmae_ctl_stop(struct sh_dmae_device *shdev) +{ + unsigned short dmaor; + unsigned long flags; + + spin_lock_irqsave(&sh_dmae_lock, flags); + + dmaor = dmaor_read(shdev); + dmaor_write(shdev, dmaor & ~(DMAOR_NMIF | DMAOR_AE | DMAOR_DME)); + + spin_unlock_irqrestore(&sh_dmae_lock, flags); +} + +static int sh_dmae_rst(struct sh_dmae_device *shdev) +{ + unsigned short dmaor; + unsigned long flags; + + spin_lock_irqsave(&sh_dmae_lock, flags); + + dmaor = dmaor_read(shdev) & ~(DMAOR_NMIF | DMAOR_AE | DMAOR_DME); + + if (shdev->pdata->chclr_present) { + int i; + for (i = 0; i < shdev->pdata->channel_num; i++) { + struct sh_dmae_chan *sh_chan = shdev->chan[i]; + if (sh_chan) + channel_clear(sh_chan); + } + } + + dmaor_write(shdev, dmaor | shdev->pdata->dmaor_init); + + dmaor = dmaor_read(shdev); + + spin_unlock_irqrestore(&sh_dmae_lock, flags); + + if (dmaor & (DMAOR_AE | DMAOR_NMIF)) { + dev_warn(shdev->shdma_dev.dma_dev.dev, "Can't initialize DMAOR.\n"); + return -EIO; + } + if (shdev->pdata->dmaor_init & ~dmaor) + dev_warn(shdev->shdma_dev.dma_dev.dev, + "DMAOR=0x%x hasn't latched the initial value 0x%x.\n", + dmaor, shdev->pdata->dmaor_init); + return 0; +} + +static bool dmae_is_busy(struct sh_dmae_chan *sh_chan) +{ + u32 chcr = chcr_read(sh_chan); + + if ((chcr & (CHCR_DE | CHCR_TE)) == CHCR_DE) + return true; /* working */ + + return false; /* waiting */ +} + +static unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan, u32 chcr) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); + const struct sh_dmae_pdata *pdata = shdev->pdata; + int cnt = ((chcr & pdata->ts_low_mask) >> pdata->ts_low_shift) | + ((chcr & pdata->ts_high_mask) >> pdata->ts_high_shift); + + if (cnt >= pdata->ts_shift_num) + cnt = 0; + + return pdata->ts_shift[cnt]; +} + +static u32 log2size_to_chcr(struct sh_dmae_chan *sh_chan, int l2size) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); + const struct sh_dmae_pdata *pdata = shdev->pdata; + int i; + + for (i = 0; i < pdata->ts_shift_num; i++) + if (pdata->ts_shift[i] == l2size) + break; + + if (i == pdata->ts_shift_num) + i = 0; + + return ((i << pdata->ts_low_shift) & pdata->ts_low_mask) | + ((i << pdata->ts_high_shift) & pdata->ts_high_mask); +} + +static void dmae_set_reg(struct sh_dmae_chan *sh_chan, struct sh_dmae_regs *hw) +{ + sh_dmae_writel(sh_chan, hw->sar, SAR); + sh_dmae_writel(sh_chan, hw->dar, DAR); + sh_dmae_writel(sh_chan, hw->tcr >> sh_chan->xmit_shift, TCR); +} + +static void dmae_start(struct sh_dmae_chan *sh_chan) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); + u32 chcr = chcr_read(sh_chan); + + if (shdev->pdata->needs_tend_set) + sh_dmae_writel(sh_chan, 0xFFFFFFFF, TEND); + + chcr |= CHCR_DE | shdev->chcr_ie_bit; + chcr_write(sh_chan, chcr & ~CHCR_TE); +} + +static void dmae_init(struct sh_dmae_chan *sh_chan) +{ + /* + * Default configuration for dual address memory-memory transfer. + * 0x400 represents auto-request. + */ + u32 chcr = DM_INC | SM_INC | 0x400 | log2size_to_chcr(sh_chan, + LOG2_DEFAULT_XFER_SIZE); + sh_chan->xmit_shift = calc_xmit_shift(sh_chan, chcr); + chcr_write(sh_chan, chcr); +} + +static int dmae_set_chcr(struct sh_dmae_chan *sh_chan, u32 val) +{ + /* If DMA is active, cannot set CHCR. TODO: remove this superfluous check */ + if (dmae_is_busy(sh_chan)) + return -EBUSY; + + sh_chan->xmit_shift = calc_xmit_shift(sh_chan, val); + chcr_write(sh_chan, val); + + return 0; +} + +static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); + const struct sh_dmae_pdata *pdata = shdev->pdata; + const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->shdma_chan.id]; + void __iomem *addr = shdev->dmars; + unsigned int shift = chan_pdata->dmars_bit; + + if (dmae_is_busy(sh_chan)) + return -EBUSY; + + if (pdata->no_dmars) + return 0; + + /* in the case of a missing DMARS resource use first memory window */ + if (!addr) + addr = shdev->chan_reg; + addr += chan_pdata->dmars; + + __raw_writew((__raw_readw(addr) & (0xff00 >> shift)) | (val << shift), + addr); + + return 0; +} + +static void sh_dmae_start_xfer(struct shdma_chan *schan, + struct shdma_desc *sdesc) +{ + struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, + shdma_chan); + struct sh_dmae_desc *sh_desc = container_of(sdesc, + struct sh_dmae_desc, shdma_desc); + dev_dbg(sh_chan->shdma_chan.dev, "Queue #%d to %d: %u@%x -> %x\n", + sdesc->async_tx.cookie, sh_chan->shdma_chan.id, + sh_desc->hw.tcr, sh_desc->hw.sar, sh_desc->hw.dar); + /* Get the ld start address from ld_queue */ + dmae_set_reg(sh_chan, &sh_desc->hw); + dmae_start(sh_chan); +} + +static bool sh_dmae_channel_busy(struct shdma_chan *schan) +{ + struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, + shdma_chan); + return dmae_is_busy(sh_chan); +} + +static void sh_dmae_setup_xfer(struct shdma_chan *schan, + int slave_id) +{ + struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, + shdma_chan); + + if (slave_id >= 0) { + const struct sh_dmae_slave_config *cfg = + sh_chan->config; + + dmae_set_dmars(sh_chan, cfg->mid_rid); + dmae_set_chcr(sh_chan, cfg->chcr); + } else { + dmae_init(sh_chan); + } +} + +/* + * Find a slave channel configuration from the contoller list by either a slave + * ID in the non-DT case, or by a MID/RID value in the DT case + */ +static const struct sh_dmae_slave_config *dmae_find_slave( + struct sh_dmae_chan *sh_chan, int match) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); + const struct sh_dmae_pdata *pdata = shdev->pdata; + const struct sh_dmae_slave_config *cfg; + int i; + + if (!sh_chan->shdma_chan.dev->of_node) { + if (match >= SH_DMA_SLAVE_NUMBER) + return NULL; + + for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++) + if (cfg->slave_id == match) + return cfg; + } else { + for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++) + if (cfg->mid_rid == match) { + sh_chan->shdma_chan.slave_id = i; + return cfg; + } + } + + return NULL; +} + +static int sh_dmae_set_slave(struct shdma_chan *schan, + int slave_id, dma_addr_t slave_addr, bool try) +{ + struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, + shdma_chan); + const struct sh_dmae_slave_config *cfg = dmae_find_slave(sh_chan, slave_id); + if (!cfg) + return -ENXIO; + + if (!try) { + sh_chan->config = cfg; + sh_chan->slave_addr = slave_addr ? : cfg->addr; + } + + return 0; +} + +static void dmae_halt(struct sh_dmae_chan *sh_chan) +{ + struct sh_dmae_device *shdev = to_sh_dev(sh_chan); + u32 chcr = chcr_read(sh_chan); + + chcr &= ~(CHCR_DE | CHCR_TE | shdev->chcr_ie_bit); + chcr_write(sh_chan, chcr); +} + +static int sh_dmae_desc_setup(struct shdma_chan *schan, + struct shdma_desc *sdesc, + dma_addr_t src, dma_addr_t dst, size_t *len) +{ + struct sh_dmae_desc *sh_desc = container_of(sdesc, + struct sh_dmae_desc, shdma_desc); + + if (*len > schan->max_xfer_len) + *len = schan->max_xfer_len; + + sh_desc->hw.sar = src; + sh_desc->hw.dar = dst; + sh_desc->hw.tcr = *len; + + return 0; +} + +static void sh_dmae_halt(struct shdma_chan *schan) +{ + struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, + shdma_chan); + dmae_halt(sh_chan); +} + +static bool sh_dmae_chan_irq(struct shdma_chan *schan, int irq) +{ + struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, + shdma_chan); + + if (!(chcr_read(sh_chan) & CHCR_TE)) + return false; + + /* DMA stop */ + dmae_halt(sh_chan); + + return true; +} + +static size_t sh_dmae_get_partial(struct shdma_chan *schan, + struct shdma_desc *sdesc) +{ + struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, + shdma_chan); + struct sh_dmae_desc *sh_desc = container_of(sdesc, + struct sh_dmae_desc, shdma_desc); + return (sh_desc->hw.tcr - sh_dmae_readl(sh_chan, TCR)) << + sh_chan->xmit_shift; +} + +/* Called from error IRQ or NMI */ +static bool sh_dmae_reset(struct sh_dmae_device *shdev) +{ + bool ret; + + /* halt the dma controller */ + sh_dmae_ctl_stop(shdev); + + /* We cannot detect, which channel caused the error, have to reset all */ + ret = shdma_reset(&shdev->shdma_dev); + + sh_dmae_rst(shdev); + + return ret; +} + +static irqreturn_t sh_dmae_err(int irq, void *data) +{ + struct sh_dmae_device *shdev = data; + + if (!(dmaor_read(shdev) & DMAOR_AE)) + return IRQ_NONE; + + sh_dmae_reset(shdev); + return IRQ_HANDLED; +} + +static bool sh_dmae_desc_completed(struct shdma_chan *schan, + struct shdma_desc *sdesc) +{ + struct sh_dmae_chan *sh_chan = container_of(schan, + struct sh_dmae_chan, shdma_chan); + struct sh_dmae_desc *sh_desc = container_of(sdesc, + struct sh_dmae_desc, shdma_desc); + u32 sar_buf = sh_dmae_readl(sh_chan, SAR); + u32 dar_buf = sh_dmae_readl(sh_chan, DAR); + + return (sdesc->direction == DMA_DEV_TO_MEM && + (sh_desc->hw.dar + sh_desc->hw.tcr) == dar_buf) || + (sdesc->direction != DMA_DEV_TO_MEM && + (sh_desc->hw.sar + sh_desc->hw.tcr) == sar_buf); +} + +static bool sh_dmae_nmi_notify(struct sh_dmae_device *shdev) +{ + /* Fast path out if NMIF is not asserted for this controller */ + if ((dmaor_read(shdev) & DMAOR_NMIF) == 0) + return false; + + return sh_dmae_reset(shdev); +} + +static int sh_dmae_nmi_handler(struct notifier_block *self, + unsigned long cmd, void *data) +{ + struct sh_dmae_device *shdev; + int ret = NOTIFY_DONE; + bool triggered; + + /* + * Only concern ourselves with NMI events. + * + * Normally we would check the die chain value, but as this needs + * to be architecture independent, check for NMI context instead. + */ + if (!in_nmi()) + return NOTIFY_DONE; + + rcu_read_lock(); + list_for_each_entry_rcu(shdev, &sh_dmae_devices, node) { + /* + * Only stop if one of the controllers has NMIF asserted, + * we do not want to interfere with regular address error + * handling or NMI events that don't concern the DMACs. + */ + triggered = sh_dmae_nmi_notify(shdev); + if (triggered == true) + ret = NOTIFY_OK; + } + rcu_read_unlock(); + + return ret; +} + +static struct notifier_block sh_dmae_nmi_notifier __read_mostly = { + .notifier_call = sh_dmae_nmi_handler, + + /* Run before NMI debug handler and KGDB */ + .priority = 1, +}; + +static int sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id, + int irq, unsigned long flags) +{ + const struct sh_dmae_channel *chan_pdata = &shdev->pdata->channel[id]; + struct shdma_dev *sdev = &shdev->shdma_dev; + struct platform_device *pdev = to_platform_device(sdev->dma_dev.dev); + struct sh_dmae_chan *sh_chan; + struct shdma_chan *schan; + int err; + + sh_chan = devm_kzalloc(sdev->dma_dev.dev, sizeof(struct sh_dmae_chan), + GFP_KERNEL); + if (!sh_chan) { + dev_err(sdev->dma_dev.dev, + "No free memory for allocating dma channels!\n"); + return -ENOMEM; + } + + schan = &sh_chan->shdma_chan; + schan->max_xfer_len = SH_DMA_TCR_MAX + 1; + + shdma_chan_probe(sdev, schan, id); + + sh_chan->base = shdev->chan_reg + chan_pdata->offset; + + /* set up channel irq */ + if (pdev->id >= 0) + snprintf(sh_chan->dev_id, sizeof(sh_chan->dev_id), + "sh-dmae%d.%d", pdev->id, id); + else + snprintf(sh_chan->dev_id, sizeof(sh_chan->dev_id), + "sh-dma%d", id); + + err = shdma_request_irq(schan, irq, flags, sh_chan->dev_id); + if (err) { + dev_err(sdev->dma_dev.dev, + "DMA channel %d request_irq error %d\n", + id, err); + goto err_no_irq; + } + + shdev->chan[id] = sh_chan; + return 0; + +err_no_irq: + /* remove from dmaengine device node */ + shdma_chan_remove(schan); + return err; +} + +static void sh_dmae_chan_remove(struct sh_dmae_device *shdev) +{ + struct dma_device *dma_dev = &shdev->shdma_dev.dma_dev; + struct shdma_chan *schan; + int i; + + shdma_for_each_chan(schan, &shdev->shdma_dev, i) { + BUG_ON(!schan); + + shdma_chan_remove(schan); + } + dma_dev->chancnt = 0; +} + +static void sh_dmae_shutdown(struct platform_device *pdev) +{ + struct sh_dmae_device *shdev = platform_get_drvdata(pdev); + sh_dmae_ctl_stop(shdev); +} + +static int sh_dmae_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int sh_dmae_runtime_resume(struct device *dev) +{ + struct sh_dmae_device *shdev = dev_get_drvdata(dev); + + return sh_dmae_rst(shdev); +} + +#ifdef CONFIG_PM +static int sh_dmae_suspend(struct device *dev) +{ + return 0; +} + +static int sh_dmae_resume(struct device *dev) +{ + struct sh_dmae_device *shdev = dev_get_drvdata(dev); + int i, ret; + + ret = sh_dmae_rst(shdev); + if (ret < 0) + dev_err(dev, "Failed to reset!\n"); + + for (i = 0; i < shdev->pdata->channel_num; i++) { + struct sh_dmae_chan *sh_chan = shdev->chan[i]; + + if (!sh_chan->shdma_chan.desc_num) + continue; + + if (sh_chan->shdma_chan.slave_id >= 0) { + const struct sh_dmae_slave_config *cfg = sh_chan->config; + dmae_set_dmars(sh_chan, cfg->mid_rid); + dmae_set_chcr(sh_chan, cfg->chcr); + } else { + dmae_init(sh_chan); + } + } + + return 0; +} +#else +#define sh_dmae_suspend NULL +#define sh_dmae_resume NULL +#endif + +const struct dev_pm_ops sh_dmae_pm = { + .suspend = sh_dmae_suspend, + .resume = sh_dmae_resume, + .runtime_suspend = sh_dmae_runtime_suspend, + .runtime_resume = sh_dmae_runtime_resume, +}; + +static dma_addr_t sh_dmae_slave_addr(struct shdma_chan *schan) +{ + struct sh_dmae_chan *sh_chan = container_of(schan, + struct sh_dmae_chan, shdma_chan); + + /* + * Implicit BUG_ON(!sh_chan->config) + * This is an exclusive slave DMA operation, may only be called after a + * successful slave configuration. + */ + return sh_chan->slave_addr; +} + +static struct shdma_desc *sh_dmae_embedded_desc(void *buf, int i) +{ + return &((struct sh_dmae_desc *)buf)[i].shdma_desc; +} + +static const struct shdma_ops sh_dmae_shdma_ops = { + .desc_completed = sh_dmae_desc_completed, + .halt_channel = sh_dmae_halt, + .channel_busy = sh_dmae_channel_busy, + .slave_addr = sh_dmae_slave_addr, + .desc_setup = sh_dmae_desc_setup, + .set_slave = sh_dmae_set_slave, + .setup_xfer = sh_dmae_setup_xfer, + .start_xfer = sh_dmae_start_xfer, + .embedded_desc = sh_dmae_embedded_desc, + .chan_irq = sh_dmae_chan_irq, + .get_partial = sh_dmae_get_partial, +}; + +static const struct of_device_id sh_dmae_of_match[] = { + {} +}; +MODULE_DEVICE_TABLE(of, sh_dmae_of_match); + +static int sh_dmae_probe(struct platform_device *pdev) +{ + const struct sh_dmae_pdata *pdata; + unsigned long irqflags = IRQF_DISABLED, + chan_flag[SH_DMAE_MAX_CHANNELS] = {}; + int errirq, chan_irq[SH_DMAE_MAX_CHANNELS]; + int err, i, irq_cnt = 0, irqres = 0, irq_cap = 0; + struct sh_dmae_device *shdev; + struct dma_device *dma_dev; + struct resource *chan, *dmars, *errirq_res, *chanirq_res; + + if (pdev->dev.of_node) + pdata = of_match_device(sh_dmae_of_match, &pdev->dev)->data; + else + pdata = pdev->dev.platform_data; + + /* get platform data */ + if (!pdata || !pdata->channel_num) + return -ENODEV; + + chan = platform_get_resource(pdev, IORESOURCE_MEM, 0); + /* DMARS area is optional */ + dmars = platform_get_resource(pdev, IORESOURCE_MEM, 1); + /* + * IRQ resources: + * 1. there always must be at least one IRQ IO-resource. On SH4 it is + * the error IRQ, in which case it is the only IRQ in this resource: + * start == end. If it is the only IRQ resource, all channels also + * use the same IRQ. + * 2. DMA channel IRQ resources can be specified one per resource or in + * ranges (start != end) + * 3. iff all events (channels and, optionally, error) on this + * controller use the same IRQ, only one IRQ resource can be + * specified, otherwise there must be one IRQ per channel, even if + * some of them are equal + * 4. if all IRQs on this controller are equal or if some specific IRQs + * specify IORESOURCE_IRQ_SHAREABLE in their resources, they will be + * requested with the IRQF_SHARED flag + */ + errirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!chan || !errirq_res) + return -ENODEV; + + shdev = devm_kzalloc(&pdev->dev, sizeof(struct sh_dmae_device), + GFP_KERNEL); + if (!shdev) { + dev_err(&pdev->dev, "Not enough memory\n"); + return -ENOMEM; + } + + dma_dev = &shdev->shdma_dev.dma_dev; + + shdev->chan_reg = devm_ioremap_resource(&pdev->dev, chan); + if (IS_ERR(shdev->chan_reg)) + return PTR_ERR(shdev->chan_reg); + if (dmars) { + shdev->dmars = devm_ioremap_resource(&pdev->dev, dmars); + if (IS_ERR(shdev->dmars)) + return PTR_ERR(shdev->dmars); + } + + if (!pdata->slave_only) + dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask); + if (pdata->slave && pdata->slave_num) + dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); + + /* Default transfer size of 32 bytes requires 32-byte alignment */ + dma_dev->copy_align = LOG2_DEFAULT_XFER_SIZE; + + shdev->shdma_dev.ops = &sh_dmae_shdma_ops; + shdev->shdma_dev.desc_size = sizeof(struct sh_dmae_desc); + err = shdma_init(&pdev->dev, &shdev->shdma_dev, + pdata->channel_num); + if (err < 0) + goto eshdma; + + /* platform data */ + shdev->pdata = pdata; + + if (pdata->chcr_offset) + shdev->chcr_offset = pdata->chcr_offset; + else + shdev->chcr_offset = CHCR; + + if (pdata->chcr_ie_bit) + shdev->chcr_ie_bit = pdata->chcr_ie_bit; + else + shdev->chcr_ie_bit = CHCR_IE; + + platform_set_drvdata(pdev, shdev); + + pm_runtime_enable(&pdev->dev); + err = pm_runtime_get_sync(&pdev->dev); + if (err < 0) + dev_err(&pdev->dev, "%s(): GET = %d\n", __func__, err); + + spin_lock_irq(&sh_dmae_lock); + list_add_tail_rcu(&shdev->node, &sh_dmae_devices); + spin_unlock_irq(&sh_dmae_lock); + + /* reset dma controller - only needed as a test */ + err = sh_dmae_rst(shdev); + if (err) + goto rst_err; + +#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) + chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + + if (!chanirq_res) + chanirq_res = errirq_res; + else + irqres++; + + if (chanirq_res == errirq_res || + (errirq_res->flags & IORESOURCE_BITS) == IORESOURCE_IRQ_SHAREABLE) + irqflags = IRQF_SHARED; + + errirq = errirq_res->start; + + err = devm_request_irq(&pdev->dev, errirq, sh_dmae_err, irqflags, + "DMAC Address Error", shdev); + if (err) { + dev_err(&pdev->dev, + "DMA failed requesting irq #%d, error %d\n", + errirq, err); + goto eirq_err; + } + +#else + chanirq_res = errirq_res; +#endif /* CONFIG_CPU_SH4 || CONFIG_ARCH_SHMOBILE */ + + if (chanirq_res->start == chanirq_res->end && + !platform_get_resource(pdev, IORESOURCE_IRQ, 1)) { + /* Special case - all multiplexed */ + for (; irq_cnt < pdata->channel_num; irq_cnt++) { + if (irq_cnt < SH_DMAE_MAX_CHANNELS) { + chan_irq[irq_cnt] = chanirq_res->start; + chan_flag[irq_cnt] = IRQF_SHARED; + } else { + irq_cap = 1; + break; + } + } + } else { + do { + for (i = chanirq_res->start; i <= chanirq_res->end; i++) { + if (irq_cnt >= SH_DMAE_MAX_CHANNELS) { + irq_cap = 1; + break; + } + + if ((errirq_res->flags & IORESOURCE_BITS) == + IORESOURCE_IRQ_SHAREABLE) + chan_flag[irq_cnt] = IRQF_SHARED; + else + chan_flag[irq_cnt] = IRQF_DISABLED; + dev_dbg(&pdev->dev, + "Found IRQ %d for channel %d\n", + i, irq_cnt); + chan_irq[irq_cnt++] = i; + } + + if (irq_cnt >= SH_DMAE_MAX_CHANNELS) + break; + + chanirq_res = platform_get_resource(pdev, + IORESOURCE_IRQ, ++irqres); + } while (irq_cnt < pdata->channel_num && chanirq_res); + } + + /* Create DMA Channel */ + for (i = 0; i < irq_cnt; i++) { + err = sh_dmae_chan_probe(shdev, i, chan_irq[i], chan_flag[i]); + if (err) + goto chan_probe_err; + } + + if (irq_cap) + dev_notice(&pdev->dev, "Attempting to register %d DMA " + "channels when a maximum of %d are supported.\n", + pdata->channel_num, SH_DMAE_MAX_CHANNELS); + + pm_runtime_put(&pdev->dev); + + err = dma_async_device_register(&shdev->shdma_dev.dma_dev); + if (err < 0) + goto edmadevreg; + + return err; + +edmadevreg: + pm_runtime_get(&pdev->dev); + +chan_probe_err: + sh_dmae_chan_remove(shdev); + +#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE) +eirq_err: +#endif +rst_err: + spin_lock_irq(&sh_dmae_lock); + list_del_rcu(&shdev->node); + spin_unlock_irq(&sh_dmae_lock); + + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + platform_set_drvdata(pdev, NULL); + shdma_cleanup(&shdev->shdma_dev); +eshdma: + synchronize_rcu(); + + return err; +} + +static int sh_dmae_remove(struct platform_device *pdev) +{ + struct sh_dmae_device *shdev = platform_get_drvdata(pdev); + struct dma_device *dma_dev = &shdev->shdma_dev.dma_dev; + struct resource *res; + int errirq = platform_get_irq(pdev, 0); + + dma_async_device_unregister(dma_dev); + + if (errirq > 0) + free_irq(errirq, shdev); + + spin_lock_irq(&sh_dmae_lock); + list_del_rcu(&shdev->node); + spin_unlock_irq(&sh_dmae_lock); + + pm_runtime_disable(&pdev->dev); + + sh_dmae_chan_remove(shdev); + shdma_cleanup(&shdev->shdma_dev); + + platform_set_drvdata(pdev, NULL); + + synchronize_rcu(); + + return 0; +} + +static struct platform_driver sh_dmae_driver = { + .driver = { + .owner = THIS_MODULE, + .pm = &sh_dmae_pm, + .name = SH_DMAE_DRV_NAME, + .of_match_table = sh_dmae_of_match, + }, + .remove = sh_dmae_remove, + .shutdown = sh_dmae_shutdown, +}; + +static int __init sh_dmae_init(void) +{ + /* Wire up NMI handling */ + int err = register_die_notifier(&sh_dmae_nmi_notifier); + if (err) + return err; + + return platform_driver_probe(&sh_dmae_driver, sh_dmae_probe); +} +module_init(sh_dmae_init); + +static void __exit sh_dmae_exit(void) +{ + platform_driver_unregister(&sh_dmae_driver); + + unregister_die_notifier(&sh_dmae_nmi_notifier); +} +module_exit(sh_dmae_exit); + +MODULE_AUTHOR("Nobuhiro Iwamatsu "); +MODULE_DESCRIPTION("Renesas SH DMA Engine driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" SH_DMAE_DRV_NAME); diff --git a/drivers/dma/sh/sudmac.c b/drivers/dma/sh/sudmac.c index 4808b45141b4..4600b429603e 100644 --- a/drivers/dma/sh/sudmac.c +++ b/drivers/dma/sh/sudmac.c @@ -150,7 +150,8 @@ static const struct sudmac_slave_config *sudmac_find_slave( return NULL; } -static int sudmac_set_slave(struct shdma_chan *schan, int slave_id, bool try) +static int sudmac_set_slave(struct shdma_chan *schan, int slave_id, + dma_addr_t slave_addr, bool try) { struct sudmac_chan *sc = to_chan(schan); const struct sudmac_slave_config *cfg = sudmac_find_slave(sc, slave_id); diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index 31cf89fb1d5b..f92c0a43c54c 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h @@ -96,7 +96,7 @@ struct shdma_ops { dma_addr_t (*slave_addr)(struct shdma_chan *); int (*desc_setup)(struct shdma_chan *, struct shdma_desc *, dma_addr_t, dma_addr_t, size_t *); - int (*set_slave)(struct shdma_chan *, int, bool); + int (*set_slave)(struct shdma_chan *, int, dma_addr_t, bool); void (*setup_xfer)(struct shdma_chan *, int); void (*start_xfer)(struct shdma_chan *, struct shdma_desc *); struct shdma_desc *(*embedded_desc)(void *, int); -- cgit v1.2.3-71-gd317 From 4620ad5419612fcd9ab412410440d3a7e8a9a90a Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 2 Aug 2013 16:50:37 +0200 Subject: DMA: shdma: remove private and unused defines from a global header Macros, named like TEND or SAR lack a namespace and are too broadly named for a global header. Besides, they aren't needed globally. Move them to where they belong - into the driver. Some other macros aren't used at all, remove them. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- drivers/dma/sh/shdmac.c | 9 +++++++++ include/linux/sh_dma.h | 21 --------------------- 2 files changed, 9 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c index a47b70879e05..03efd4ad0f0b 100644 --- a/drivers/dma/sh/shdmac.c +++ b/drivers/dma/sh/shdmac.c @@ -37,6 +37,15 @@ #include "../dmaengine.h" #include "shdma.h" +/* DMA register */ +#define SAR 0x00 +#define DAR 0x04 +#define TCR 0x08 +#define CHCR 0x0C +#define DMAOR 0x40 + +#define TEND 0x18 /* USB-DMAC */ + #define SH_DMAE_DRV_NAME "sh-dma-engine" /* Default MEMCPY transfer size = 2^2 = 4 bytes */ diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h index 776ed9d682f4..b7b43b82231e 100644 --- a/include/linux/sh_dma.h +++ b/include/linux/sh_dma.h @@ -94,39 +94,18 @@ struct sh_dmae_pdata { unsigned int slave_only:1; }; -/* DMA register */ -#define SAR 0x00 -#define DAR 0x04 -#define TCR 0x08 -#define CHCR 0x0C -#define DMAOR 0x40 - -#define TEND 0x18 /* USB-DMAC */ - /* DMAOR definitions */ #define DMAOR_AE 0x00000004 #define DMAOR_NMIF 0x00000002 #define DMAOR_DME 0x00000001 /* Definitions for the SuperH DMAC */ -#define REQ_L 0x00000000 -#define REQ_E 0x00080000 -#define RACK_H 0x00000000 -#define RACK_L 0x00040000 -#define ACK_R 0x00000000 -#define ACK_W 0x00020000 -#define ACK_H 0x00000000 -#define ACK_L 0x00010000 #define DM_INC 0x00004000 #define DM_DEC 0x00008000 #define DM_FIX 0x0000c000 #define SM_INC 0x00001000 #define SM_DEC 0x00002000 #define SM_FIX 0x00003000 -#define RS_IN 0x00000200 -#define RS_OUT 0x00000300 -#define TS_BLK 0x00000040 -#define TM_BUR 0x00000020 #define CHCR_DE 0x00000001 #define CHCR_TE 0x00000002 #define CHCR_IE 0x00000004 -- cgit v1.2.3-71-gd317 From 824ef826f3c4d83d1925a5e351313bfd3e5ca6cb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 27 Aug 2013 15:51:01 +0200 Subject: ASoC: Pass card instead of dapm context to snd_soc_dapm_new_widgets() snd_soc_dapm_new_widgets() works on the ASoC card as a whole not on a specific DAPM context. The DAPM context that is passed as the parameter is only used to look up the pointer to the card. This patch updates the signature of snd_soc_dapm_new_widgets() to take the card directly. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 2 +- sound/soc/soc-core.c | 2 +- sound/soc/soc-dapm.c | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index c728d28ae9a5..27a72d5d4b00 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -413,7 +413,7 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, struct snd_soc_dapm_widget *sink); /* dapm path setup */ -int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm); +int snd_soc_dapm_new_widgets(struct snd_soc_card *card); void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, const struct snd_soc_dapm_route *route, int num); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index d476f752e3f1..ed3c253066b1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1807,7 +1807,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) list_for_each_entry(codec, &card->codec_dev_list, card_list) snd_soc_dapm_auto_nc_codec_pins(codec); - snd_soc_dapm_new_widgets(&card->dapm); + snd_soc_dapm_new_widgets(card); ret = snd_card_register(card->snd_card); if (ret < 0) { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7e9afbc49ef2..548b1c9e875e 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2712,9 +2712,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes); * * Returns 0 for success. */ -int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) +int snd_soc_dapm_new_widgets(struct snd_soc_card *card) { - struct snd_soc_card *card = dapm->card; struct snd_soc_dapm_widget *w; unsigned int val; -- cgit v1.2.3-71-gd317 From d2ab1fa68c61f01b28ab0859a972c892d81f5d32 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 27 Aug 2013 11:11:10 -0600 Subject: PCI: Rename PCIe capability definitions to follow convention All other PCIe capability register fields include "PCI_EXP" + + . This renames PCI_EXP_OBFF_MASK, PCI_EXP_IDO_REQ_EN, PCI_EXP_LTR_EN, and related fields using the same convention. No functional change. Signed-off-by: Bjorn Helgaas Acked-by: Samuel Ortiz # for MFD driver --- drivers/mfd/rts5227.c | 2 +- drivers/pci/pci.c | 31 +++++++++++++++++-------------- include/uapi/linux/pci_regs.h | 24 ++++++++++++------------ 3 files changed, 30 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/mfd/rts5227.c b/drivers/mfd/rts5227.c index fc831dcb1480..164b7faa70c9 100644 --- a/drivers/mfd/rts5227.c +++ b/drivers/mfd/rts5227.c @@ -44,7 +44,7 @@ static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02); /* Configure LTR */ pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &cap); - if (cap & PCI_EXP_LTR_EN) + if (cap & PCI_EXP_DEVCTL2_LTR_EN) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LTR_CTL, 0xFF, 0xA3); /* Configure OBFF */ rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OBFF_CFG, 0x03, 0x03); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 42e5f86e2387..3d5d45cdc4dd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2095,9 +2095,9 @@ void pci_enable_ido(struct pci_dev *dev, unsigned long type) u16 ctrl = 0; if (type & PCI_EXP_IDO_REQUEST) - ctrl |= PCI_EXP_IDO_REQ_EN; + ctrl |= PCI_EXP_DEVCTL2_IDO_REQ_EN; if (type & PCI_EXP_IDO_COMPLETION) - ctrl |= PCI_EXP_IDO_CMP_EN; + ctrl |= PCI_EXP_DEVCTL2_IDO_CMP_EN; if (ctrl) pcie_capability_set_word(dev, PCI_EXP_DEVCTL2, ctrl); } @@ -2113,9 +2113,9 @@ void pci_disable_ido(struct pci_dev *dev, unsigned long type) u16 ctrl = 0; if (type & PCI_EXP_IDO_REQUEST) - ctrl |= PCI_EXP_IDO_REQ_EN; + ctrl |= PCI_EXP_DEVCTL2_IDO_REQ_EN; if (type & PCI_EXP_IDO_COMPLETION) - ctrl |= PCI_EXP_IDO_CMP_EN; + ctrl |= PCI_EXP_DEVCTL2_IDO_CMP_EN; if (ctrl) pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, ctrl); } @@ -2147,7 +2147,7 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type) int ret; pcie_capability_read_dword(dev, PCI_EXP_DEVCAP2, &cap); - if (!(cap & PCI_EXP_OBFF_MASK)) + if (!(cap & PCI_EXP_DEVCAP2_OBFF_MASK)) return -ENOTSUPP; /* no OBFF support at all */ /* Make sure the topology supports OBFF as well */ @@ -2158,17 +2158,17 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type) } pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &ctrl); - if (cap & PCI_EXP_OBFF_WAKE) - ctrl |= PCI_EXP_OBFF_WAKE_EN; + if (cap & PCI_EXP_DEVCAP2_OBFF_WAKE) + ctrl |= PCI_EXP_DEVCTL2_OBFF_WAKE_EN; else { switch (type) { case PCI_EXP_OBFF_SIGNAL_L0: - if (!(ctrl & PCI_EXP_OBFF_WAKE_EN)) - ctrl |= PCI_EXP_OBFF_MSGA_EN; + if (!(ctrl & PCI_EXP_DEVCTL2_OBFF_WAKE_EN)) + ctrl |= PCI_EXP_DEVCTL2_OBFF_MSGA_EN; break; case PCI_EXP_OBFF_SIGNAL_ALWAYS: - ctrl &= ~PCI_EXP_OBFF_WAKE_EN; - ctrl |= PCI_EXP_OBFF_MSGB_EN; + ctrl &= ~PCI_EXP_DEVCTL2_OBFF_WAKE_EN; + ctrl |= PCI_EXP_DEVCTL2_OBFF_MSGB_EN; break; default: WARN(1, "bad OBFF signal type\n"); @@ -2189,7 +2189,8 @@ EXPORT_SYMBOL(pci_enable_obff); */ void pci_disable_obff(struct pci_dev *dev) { - pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, PCI_EXP_OBFF_WAKE_EN); + pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_OBFF_WAKE_EN); } EXPORT_SYMBOL(pci_disable_obff); @@ -2237,7 +2238,8 @@ int pci_enable_ltr(struct pci_dev *dev) return ret; } - return pcie_capability_set_word(dev, PCI_EXP_DEVCTL2, PCI_EXP_LTR_EN); + return pcie_capability_set_word(dev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_LTR_EN); } EXPORT_SYMBOL(pci_enable_ltr); @@ -2254,7 +2256,8 @@ void pci_disable_ltr(struct pci_dev *dev) if (!pci_ltr_supported(dev)) return; - pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, PCI_EXP_LTR_EN); + pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_LTR_EN); } EXPORT_SYMBOL(pci_disable_ltr); diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index c3cc01d474b0..4b8f2e3bad58 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -550,19 +550,19 @@ * to use these fields safely. */ #define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ -#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ -#define PCI_EXP_DEVCAP2_LTR 0x800 /* Latency tolerance reporting */ -#define PCI_EXP_OBFF_MASK 0xc0000 /* OBFF support mechanism */ -#define PCI_EXP_OBFF_MSG 0x40000 /* New message signaling */ -#define PCI_EXP_OBFF_WAKE 0x80000 /* Re-use WAKE# for OBFF */ +#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_DEVCAP2_LTR 0x800 /* Latency tolerance reporting */ +#define PCI_EXP_DEVCAP2_OBFF_MASK 0xc0000 /* OBFF support mechanism */ +#define PCI_EXP_DEVCAP2_OBFF_MSG 0x40000 /* New message signaling */ +#define PCI_EXP_DEVCAP2_OBFF_WAKE 0x80000 /* Re-use WAKE# for OBFF */ #define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ -#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ -#define PCI_EXP_IDO_REQ_EN 0x100 /* ID-based ordering request enable */ -#define PCI_EXP_IDO_CMP_EN 0x200 /* ID-based ordering completion enable */ -#define PCI_EXP_LTR_EN 0x400 /* Latency tolerance reporting */ -#define PCI_EXP_OBFF_MSGA_EN 0x2000 /* OBFF enable with Message type A */ -#define PCI_EXP_OBFF_MSGB_EN 0x4000 /* OBFF enable with Message type B */ -#define PCI_EXP_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ +#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_DEVCTL2_IDO_REQ_EN 0x100 /* ID-based ordering request enable */ +#define PCI_EXP_DEVCTL2_IDO_CMP_EN 0x200 /* ID-based ordering completion enable */ +#define PCI_EXP_DEVCTL2_LTR_EN 0x400 /* Latency tolerance reporting */ +#define PCI_EXP_DEVCTL2_OBFF_MSGA_EN 0x2000 /* OBFF enable with Message type A */ +#define PCI_EXP_DEVCTL2_OBFF_MSGB_EN 0x4000 /* OBFF enable with Message type B */ +#define PCI_EXP_DEVCTL2_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 44 /* v2 endpoints end here */ #define PCI_EXP_LNKCAP2 44 /* Link Capability 2 */ #define PCI_EXP_LNKCAP2_SLS_2_5GB 0x02 /* Supported Link Speed 2.5GT/s */ -- cgit v1.2.3-71-gd317 From 41d73ec053d2424599c4ed8452b889374d523ade Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 27 Aug 2013 08:50:12 +0200 Subject: netfilter: nf_conntrack: make sequence number adjustments usuable without NAT Split out sequence number adjustments from NAT and move them to the conntrack core to make them usable for SYN proxying. The sequence number adjustment information is moved to a seperate extend. The extend is added to new conntracks when a NAT mapping is set up for a connection using a helper. As a side effect, this saves 24 bytes per connection with NAT in the common case that a connection does not have a helper assigned. Signed-off-by: Patrick McHardy Tested-by: Martin Topholm Signed-off-by: Jesper Dangaard Brouer Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter.h | 9 +- include/net/netfilter/nf_conntrack_extend.h | 2 + include/net/netfilter/nf_conntrack_seqadj.h | 49 +++++ include/net/netfilter/nf_nat.h | 10 - include/net/netfilter/nf_nat_helper.h | 19 -- include/uapi/linux/netfilter/nf_conntrack_common.h | 3 +- include/uapi/linux/netfilter/nfnetlink_conntrack.h | 15 +- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 7 +- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 7 +- net/netfilter/Makefile | 2 +- net/netfilter/nf_conntrack_core.c | 16 +- net/netfilter/nf_conntrack_netlink.c | 115 +++++------ net/netfilter/nf_conntrack_proto_tcp.c | 18 +- net/netfilter/nf_conntrack_seqadj.c | 218 ++++++++++++++++++++ net/netfilter/nf_nat_core.c | 16 +- net/netfilter/nf_nat_helper.c | 228 +-------------------- net/netfilter/nf_nat_sip.c | 3 +- net/netfilter/nfnetlink_queue_ct.c | 8 +- 18 files changed, 369 insertions(+), 376 deletions(-) create mode 100644 include/net/netfilter/nf_conntrack_seqadj.h create mode 100644 net/netfilter/nf_conntrack_seqadj.c (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index e2cf786be22f..708fe72ab913 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -319,6 +319,7 @@ extern void nf_ct_attach(struct sk_buff *, const struct sk_buff *); extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu; struct nf_conn; +enum ip_conntrack_info; struct nlattr; struct nfq_ct_hook { @@ -327,14 +328,10 @@ struct nfq_ct_hook { int (*parse)(const struct nlattr *attr, struct nf_conn *ct); int (*attach_expect)(const struct nlattr *attr, struct nf_conn *ct, u32 portid, u32 report); -}; -extern struct nfq_ct_hook __rcu *nfq_ct_hook; - -struct nfq_ct_nat_hook { void (*seq_adjust)(struct sk_buff *skb, struct nf_conn *ct, - u32 ctinfo, s32 off); + enum ip_conntrack_info ctinfo, s32 off); }; -extern struct nfq_ct_nat_hook __rcu *nfq_ct_nat_hook; +extern struct nfq_ct_hook __rcu *nfq_ct_hook; #else static inline void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb) {} #endif diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 977bc8a46444..2a22bcbfe6e4 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -9,6 +9,7 @@ enum nf_ct_ext_id { NF_CT_EXT_HELPER, #if defined(CONFIG_NF_NAT) || defined(CONFIG_NF_NAT_MODULE) NF_CT_EXT_NAT, + NF_CT_EXT_SEQADJ, #endif NF_CT_EXT_ACCT, #ifdef CONFIG_NF_CONNTRACK_EVENTS @@ -31,6 +32,7 @@ enum nf_ct_ext_id { #define NF_CT_EXT_HELPER_TYPE struct nf_conn_help #define NF_CT_EXT_NAT_TYPE struct nf_conn_nat +#define NF_CT_EXT_SEQADJ_TYPE struct nf_conn_seqadj #define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter #define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone diff --git a/include/net/netfilter/nf_conntrack_seqadj.h b/include/net/netfilter/nf_conntrack_seqadj.h new file mode 100644 index 000000000000..30bfbbed9f47 --- /dev/null +++ b/include/net/netfilter/nf_conntrack_seqadj.h @@ -0,0 +1,49 @@ +#ifndef _NF_CONNTRACK_SEQADJ_H +#define _NF_CONNTRACK_SEQADJ_H + +#include + +/** + * struct nf_ct_seqadj - sequence number adjustment information + * + * @correction_pos: position of the last TCP sequence number modification + * @offset_before: sequence number offset before last modification + * @offset_after: sequence number offset after last modification + */ +struct nf_ct_seqadj { + u32 correction_pos; + s32 offset_before; + s32 offset_after; +}; + +struct nf_conn_seqadj { + struct nf_ct_seqadj seq[IP_CT_DIR_MAX]; +}; + +static inline struct nf_conn_seqadj *nfct_seqadj(const struct nf_conn *ct) +{ + return nf_ct_ext_find(ct, NF_CT_EXT_SEQADJ); +} + +static inline struct nf_conn_seqadj *nfct_seqadj_ext_add(struct nf_conn *ct) +{ + return nf_ct_ext_add(ct, NF_CT_EXT_SEQADJ, GFP_ATOMIC); +} + +extern int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo, + __be32 seq, s32 off); +extern void nf_ct_tcp_seqadj_set(struct sk_buff *skb, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + s32 off); + +extern int nf_ct_seq_adjust(struct sk_buff *skb, + struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff); +extern s32 nf_ct_seq_offset(const struct nf_conn *ct, enum ip_conntrack_dir, + u32 seq); + +extern int nf_conntrack_seqadj_init(void); +extern void nf_conntrack_seqadj_fini(void); + +#endif /* _NF_CONNTRACK_SEQADJ_H */ diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index e2441413675c..59a192420053 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -13,15 +13,6 @@ enum nf_nat_manip_type { #define HOOK2MANIP(hooknum) ((hooknum) != NF_INET_POST_ROUTING && \ (hooknum) != NF_INET_LOCAL_IN) -/* NAT sequence number modifications */ -struct nf_nat_seq { - /* position of the last TCP sequence number modification (if any) */ - u_int32_t correction_pos; - - /* sequence number offset before and after last modification */ - int32_t offset_before, offset_after; -}; - #include #include #include @@ -39,7 +30,6 @@ struct nf_conn; /* The structure embedded in the conntrack structure. */ struct nf_conn_nat { struct hlist_node bysource; - struct nf_nat_seq seq[IP_CT_DIR_MAX]; struct nf_conn *ct; union nf_conntrack_nat_help help; #if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \ diff --git a/include/net/netfilter/nf_nat_helper.h b/include/net/netfilter/nf_nat_helper.h index 194c34794923..404324d1d0c4 100644 --- a/include/net/netfilter/nf_nat_helper.h +++ b/include/net/netfilter/nf_nat_helper.h @@ -39,28 +39,9 @@ extern int nf_nat_mangle_udp_packet(struct sk_buff *skb, const char *rep_buffer, unsigned int rep_len); -extern void nf_nat_set_seq_adjust(struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - __be32 seq, s32 off); -extern int nf_nat_seq_adjust(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff); -extern int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff); - /* Setup NAT on this expected conntrack so it follows master, but goes * to port ct->master->saved_proto. */ extern void nf_nat_follow_master(struct nf_conn *ct, struct nf_conntrack_expect *this); -extern s32 nf_nat_get_offset(const struct nf_conn *ct, - enum ip_conntrack_dir dir, - u32 seq); - -extern void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, - u32 dir, s32 off); - #endif diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h index d69483fb3825..8dd803818ebe 100644 --- a/include/uapi/linux/netfilter/nf_conntrack_common.h +++ b/include/uapi/linux/netfilter/nf_conntrack_common.h @@ -99,7 +99,8 @@ enum ip_conntrack_events { IPCT_PROTOINFO, /* protocol information has changed */ IPCT_HELPER, /* new helper has been set */ IPCT_MARK, /* new mark has been set */ - IPCT_NATSEQADJ, /* NAT is doing sequence adjustment */ + IPCT_SEQADJ, /* sequence adjustment has changed */ + IPCT_NATSEQADJ = IPCT_SEQADJ, IPCT_SECMARK, /* new security mark has been set */ IPCT_LABEL, /* new connlabel has been set */ }; diff --git a/include/uapi/linux/netfilter/nfnetlink_conntrack.h b/include/uapi/linux/netfilter/nfnetlink_conntrack.h index 08fabc6c93f3..acad6c52a652 100644 --- a/include/uapi/linux/netfilter/nfnetlink_conntrack.h +++ b/include/uapi/linux/netfilter/nfnetlink_conntrack.h @@ -42,8 +42,10 @@ enum ctattr_type { CTA_ID, CTA_NAT_DST, CTA_TUPLE_MASTER, - CTA_NAT_SEQ_ADJ_ORIG, - CTA_NAT_SEQ_ADJ_REPLY, + CTA_SEQ_ADJ_ORIG, + CTA_NAT_SEQ_ADJ_ORIG = CTA_SEQ_ADJ_ORIG, + CTA_SEQ_ADJ_REPLY, + CTA_NAT_SEQ_ADJ_REPLY = CTA_SEQ_ADJ_REPLY, CTA_SECMARK, /* obsolete */ CTA_ZONE, CTA_SECCTX, @@ -165,6 +167,15 @@ enum ctattr_protonat { }; #define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1) +enum ctattr_seqadj { + CTA_SEQADJ_UNSPEC, + CTA_SEQADJ_CORRECTION_POS, + CTA_SEQADJ_OFFSET_BEFORE, + CTA_SEQADJ_OFFSET_AFTER, + __CTA_SEQADJ_MAX +}; +#define CTA_SEQADJ_MAX (__CTA_SEQADJ_MAX - 1) + enum ctattr_natseq { CTA_NAT_SEQ_UNSPEC, CTA_NAT_SEQ_CORRECTION_POS, diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 0a2e0e3e95ba..86f5b34a4ed1 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -136,11 +137,7 @@ static unsigned int ipv4_confirm(unsigned int hooknum, /* adjust seqs for loopback traffic only in outgoing direction */ if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && !nf_is_loopback_packet(skb)) { - typeof(nf_nat_seq_adjust_hook) seq_adjust; - - seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook); - if (!seq_adjust || - !seq_adjust(skb, ct, ctinfo, ip_hdrlen(skb))) { + if (!nf_ct_seq_adjust(skb, ct, ctinfo, ip_hdrlen(skb))) { NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); return NF_DROP; } diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index c9b6a6e6a1e8..d6e4dd8b58df 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -158,11 +159,7 @@ static unsigned int ipv6_confirm(unsigned int hooknum, /* adjust seqs for loopback traffic only in outgoing direction */ if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && !nf_is_loopback_packet(skb)) { - typeof(nf_nat_seq_adjust_hook) seq_adjust; - - seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook); - if (!seq_adjust || - !seq_adjust(skb, ct, ctinfo, protoff)) { + if (!nf_ct_seq_adjust(skb, ct, ctinfo, protoff)) { NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); return NF_DROP; } diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index ebfa7dc747cd..89a9c1658f5e 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -1,6 +1,6 @@ netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o -nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o +nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o nf_conntrack_seqadj.o nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index da6f1787a102..00a7a94d4132 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -1326,6 +1327,7 @@ void nf_conntrack_cleanup_end(void) nf_ct_extend_unregister(&nf_ct_zone_extend); #endif nf_conntrack_proto_fini(); + nf_conntrack_seqadj_fini(); nf_conntrack_labels_fini(); nf_conntrack_helper_fini(); nf_conntrack_timeout_fini(); @@ -1531,6 +1533,10 @@ int nf_conntrack_init_start(void) if (ret < 0) goto err_labels; + ret = nf_conntrack_seqadj_init(); + if (ret < 0) + goto err_seqadj; + #ifdef CONFIG_NF_CONNTRACK_ZONES ret = nf_ct_extend_register(&nf_ct_zone_extend); if (ret < 0) @@ -1555,6 +1561,8 @@ err_proto: nf_ct_extend_unregister(&nf_ct_zone_extend); err_extend: #endif + nf_conntrack_seqadj_fini(); +err_seqadj: nf_conntrack_labels_fini(); err_labels: nf_conntrack_helper_fini(); @@ -1577,9 +1585,6 @@ void nf_conntrack_init_end(void) /* For use by REJECT target */ RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach); RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack); - - /* Howto get NAT offsets */ - RCU_INIT_POINTER(nf_ct_nat_offset, NULL); } /* @@ -1666,8 +1671,3 @@ err_slabname: err_stat: return ret; } - -s32 (*nf_ct_nat_offset)(const struct nf_conn *ct, - enum ip_conntrack_dir dir, - u32 seq); -EXPORT_SYMBOL_GPL(nf_ct_nat_offset); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index fa61fea63234..7c55745ececf 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -381,9 +382,8 @@ nla_put_failure: return -1; } -#ifdef CONFIG_NF_NAT_NEEDED static int -dump_nat_seq_adj(struct sk_buff *skb, const struct nf_nat_seq *natseq, int type) +dump_ct_seq_adj(struct sk_buff *skb, const struct nf_ct_seqadj *seq, int type) { struct nlattr *nest_parms; @@ -391,12 +391,12 @@ dump_nat_seq_adj(struct sk_buff *skb, const struct nf_nat_seq *natseq, int type) if (!nest_parms) goto nla_put_failure; - if (nla_put_be32(skb, CTA_NAT_SEQ_CORRECTION_POS, - htonl(natseq->correction_pos)) || - nla_put_be32(skb, CTA_NAT_SEQ_OFFSET_BEFORE, - htonl(natseq->offset_before)) || - nla_put_be32(skb, CTA_NAT_SEQ_OFFSET_AFTER, - htonl(natseq->offset_after))) + if (nla_put_be32(skb, CTA_SEQADJ_CORRECTION_POS, + htonl(seq->correction_pos)) || + nla_put_be32(skb, CTA_SEQADJ_OFFSET_BEFORE, + htonl(seq->offset_before)) || + nla_put_be32(skb, CTA_SEQADJ_OFFSET_AFTER, + htonl(seq->offset_after))) goto nla_put_failure; nla_nest_end(skb, nest_parms); @@ -408,27 +408,24 @@ nla_put_failure: } static inline int -ctnetlink_dump_nat_seq_adj(struct sk_buff *skb, const struct nf_conn *ct) +ctnetlink_dump_ct_seq_adj(struct sk_buff *skb, const struct nf_conn *ct) { - struct nf_nat_seq *natseq; - struct nf_conn_nat *nat = nfct_nat(ct); + struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); + struct nf_ct_seqadj *seq; - if (!(ct->status & IPS_SEQ_ADJUST) || !nat) + if (!(ct->status & IPS_SEQ_ADJUST) || !seqadj) return 0; - natseq = &nat->seq[IP_CT_DIR_ORIGINAL]; - if (dump_nat_seq_adj(skb, natseq, CTA_NAT_SEQ_ADJ_ORIG) == -1) + seq = &seqadj->seq[IP_CT_DIR_ORIGINAL]; + if (dump_ct_seq_adj(skb, seq, CTA_SEQ_ADJ_ORIG) == -1) return -1; - natseq = &nat->seq[IP_CT_DIR_REPLY]; - if (dump_nat_seq_adj(skb, natseq, CTA_NAT_SEQ_ADJ_REPLY) == -1) + seq = &seqadj->seq[IP_CT_DIR_REPLY]; + if (dump_ct_seq_adj(skb, seq, CTA_SEQ_ADJ_REPLY) == -1) return -1; return 0; } -#else -#define ctnetlink_dump_nat_seq_adj(a, b) (0) -#endif static inline int ctnetlink_dump_id(struct sk_buff *skb, const struct nf_conn *ct) @@ -502,7 +499,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, ctnetlink_dump_id(skb, ct) < 0 || ctnetlink_dump_use(skb, ct) < 0 || ctnetlink_dump_master(skb, ct) < 0 || - ctnetlink_dump_nat_seq_adj(skb, ct) < 0) + ctnetlink_dump_ct_seq_adj(skb, ct) < 0) goto nla_put_failure; nlmsg_end(skb, nlh); @@ -707,8 +704,8 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) ctnetlink_dump_master(skb, ct) < 0) goto nla_put_failure; - if (events & (1 << IPCT_NATSEQADJ) && - ctnetlink_dump_nat_seq_adj(skb, ct) < 0) + if (events & (1 << IPCT_SEQADJ) && + ctnetlink_dump_ct_seq_adj(skb, ct) < 0) goto nla_put_failure; } @@ -1439,66 +1436,65 @@ ctnetlink_change_protoinfo(struct nf_conn *ct, const struct nlattr * const cda[] return err; } -#ifdef CONFIG_NF_NAT_NEEDED -static const struct nla_policy nat_seq_policy[CTA_NAT_SEQ_MAX+1] = { - [CTA_NAT_SEQ_CORRECTION_POS] = { .type = NLA_U32 }, - [CTA_NAT_SEQ_OFFSET_BEFORE] = { .type = NLA_U32 }, - [CTA_NAT_SEQ_OFFSET_AFTER] = { .type = NLA_U32 }, +static const struct nla_policy seqadj_policy[CTA_SEQADJ_MAX+1] = { + [CTA_SEQADJ_CORRECTION_POS] = { .type = NLA_U32 }, + [CTA_SEQADJ_OFFSET_BEFORE] = { .type = NLA_U32 }, + [CTA_SEQADJ_OFFSET_AFTER] = { .type = NLA_U32 }, }; static inline int -change_nat_seq_adj(struct nf_nat_seq *natseq, const struct nlattr * const attr) +change_seq_adj(struct nf_ct_seqadj *seq, const struct nlattr * const attr) { int err; - struct nlattr *cda[CTA_NAT_SEQ_MAX+1]; + struct nlattr *cda[CTA_SEQADJ_MAX+1]; - err = nla_parse_nested(cda, CTA_NAT_SEQ_MAX, attr, nat_seq_policy); + err = nla_parse_nested(cda, CTA_SEQADJ_MAX, attr, seqadj_policy); if (err < 0) return err; - if (!cda[CTA_NAT_SEQ_CORRECTION_POS]) + if (!cda[CTA_SEQADJ_CORRECTION_POS]) return -EINVAL; - natseq->correction_pos = - ntohl(nla_get_be32(cda[CTA_NAT_SEQ_CORRECTION_POS])); + seq->correction_pos = + ntohl(nla_get_be32(cda[CTA_SEQADJ_CORRECTION_POS])); - if (!cda[CTA_NAT_SEQ_OFFSET_BEFORE]) + if (!cda[CTA_SEQADJ_OFFSET_BEFORE]) return -EINVAL; - natseq->offset_before = - ntohl(nla_get_be32(cda[CTA_NAT_SEQ_OFFSET_BEFORE])); + seq->offset_before = + ntohl(nla_get_be32(cda[CTA_SEQADJ_OFFSET_BEFORE])); - if (!cda[CTA_NAT_SEQ_OFFSET_AFTER]) + if (!cda[CTA_SEQADJ_OFFSET_AFTER]) return -EINVAL; - natseq->offset_after = - ntohl(nla_get_be32(cda[CTA_NAT_SEQ_OFFSET_AFTER])); + seq->offset_after = + ntohl(nla_get_be32(cda[CTA_SEQADJ_OFFSET_AFTER])); return 0; } static int -ctnetlink_change_nat_seq_adj(struct nf_conn *ct, - const struct nlattr * const cda[]) +ctnetlink_change_seq_adj(struct nf_conn *ct, + const struct nlattr * const cda[]) { + struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); int ret = 0; - struct nf_conn_nat *nat = nfct_nat(ct); - if (!nat) + if (!seqadj) return 0; - if (cda[CTA_NAT_SEQ_ADJ_ORIG]) { - ret = change_nat_seq_adj(&nat->seq[IP_CT_DIR_ORIGINAL], - cda[CTA_NAT_SEQ_ADJ_ORIG]); + if (cda[CTA_SEQ_ADJ_ORIG]) { + ret = change_seq_adj(&seqadj->seq[IP_CT_DIR_ORIGINAL], + cda[CTA_SEQ_ADJ_ORIG]); if (ret < 0) return ret; ct->status |= IPS_SEQ_ADJUST; } - if (cda[CTA_NAT_SEQ_ADJ_REPLY]) { - ret = change_nat_seq_adj(&nat->seq[IP_CT_DIR_REPLY], - cda[CTA_NAT_SEQ_ADJ_REPLY]); + if (cda[CTA_SEQ_ADJ_REPLY]) { + ret = change_seq_adj(&seqadj->seq[IP_CT_DIR_REPLY], + cda[CTA_SEQ_ADJ_REPLY]); if (ret < 0) return ret; @@ -1507,7 +1503,6 @@ ctnetlink_change_nat_seq_adj(struct nf_conn *ct, return 0; } -#endif static int ctnetlink_attach_labels(struct nf_conn *ct, const struct nlattr * const cda[]) @@ -1573,13 +1568,12 @@ ctnetlink_change_conntrack(struct nf_conn *ct, ct->mark = ntohl(nla_get_be32(cda[CTA_MARK])); #endif -#ifdef CONFIG_NF_NAT_NEEDED - if (cda[CTA_NAT_SEQ_ADJ_ORIG] || cda[CTA_NAT_SEQ_ADJ_REPLY]) { - err = ctnetlink_change_nat_seq_adj(ct, cda); + if (cda[CTA_SEQ_ADJ_ORIG] || cda[CTA_SEQ_ADJ_REPLY]) { + err = ctnetlink_change_seq_adj(ct, cda); if (err < 0) return err; } -#endif + if (cda[CTA_LABELS]) { err = ctnetlink_attach_labels(ct, cda); if (err < 0) @@ -1684,13 +1678,11 @@ ctnetlink_create_conntrack(struct net *net, u16 zone, goto err2; } -#ifdef CONFIG_NF_NAT_NEEDED - if (cda[CTA_NAT_SEQ_ADJ_ORIG] || cda[CTA_NAT_SEQ_ADJ_REPLY]) { - err = ctnetlink_change_nat_seq_adj(ct, cda); + if (cda[CTA_SEQ_ADJ_ORIG] || cda[CTA_SEQ_ADJ_REPLY]) { + err = ctnetlink_change_seq_adj(ct, cda); if (err < 0) goto err2; } -#endif memset(&ct->proto, 0, sizeof(ct->proto)); if (cda[CTA_PROTOINFO]) { @@ -1804,7 +1796,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, (1 << IPCT_ASSURED) | (1 << IPCT_HELPER) | (1 << IPCT_PROTOINFO) | - (1 << IPCT_NATSEQADJ) | + (1 << IPCT_SEQADJ) | (1 << IPCT_MARK) | events, ct, NETLINK_CB(skb).portid, nlmsg_report(nlh)); @@ -1827,7 +1819,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, (1 << IPCT_HELPER) | (1 << IPCT_LABEL) | (1 << IPCT_PROTOINFO) | - (1 << IPCT_NATSEQADJ) | + (1 << IPCT_SEQADJ) | (1 << IPCT_MARK), ct, NETLINK_CB(skb).portid, nlmsg_report(nlh)); @@ -2082,7 +2074,7 @@ ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct) goto nla_put_failure; if ((ct->status & IPS_SEQ_ADJUST) && - ctnetlink_dump_nat_seq_adj(skb, ct) < 0) + ctnetlink_dump_ct_seq_adj(skb, ct) < 0) goto nla_put_failure; #ifdef CONFIG_NF_CONNTRACK_MARK @@ -2211,6 +2203,7 @@ static struct nfq_ct_hook ctnetlink_nfqueue_hook = { .build = ctnetlink_nfqueue_build, .parse = ctnetlink_nfqueue_parse, .attach_expect = ctnetlink_nfqueue_attach_expect, + .seq_adjust = nf_ct_tcp_seqadj_set, }; #endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */ diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index d224e001f14f..984a8d1a3359 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -495,21 +496,6 @@ static void tcp_sack(const struct sk_buff *skb, unsigned int dataoff, } } -#ifdef CONFIG_NF_NAT_NEEDED -static inline s32 nat_offset(const struct nf_conn *ct, - enum ip_conntrack_dir dir, - u32 seq) -{ - typeof(nf_ct_nat_offset) get_offset = rcu_dereference(nf_ct_nat_offset); - - return get_offset != NULL ? get_offset(ct, dir, seq) : 0; -} -#define NAT_OFFSET(ct, dir, seq) \ - (nat_offset(ct, dir, seq)) -#else -#define NAT_OFFSET(ct, dir, seq) 0 -#endif - static bool tcp_in_window(const struct nf_conn *ct, struct ip_ct_tcp *state, enum ip_conntrack_dir dir, @@ -540,7 +526,7 @@ static bool tcp_in_window(const struct nf_conn *ct, tcp_sack(skb, dataoff, tcph, &sack); /* Take into account NAT sequence number mangling */ - receiver_offset = NAT_OFFSET(ct, !dir, ack - 1); + receiver_offset = nf_ct_seq_offset(ct, !dir, ack - 1); ack -= receiver_offset; sack -= receiver_offset; diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c new file mode 100644 index 000000000000..483eb9ce3216 --- /dev/null +++ b/net/netfilter/nf_conntrack_seqadj.c @@ -0,0 +1,218 @@ +#include +#include +#include + +#include +#include +#include + +int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo, + __be32 seq, s32 off) +{ + struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_ct_seqadj *this_way; + + if (off == 0) + return 0; + + set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); + + spin_lock_bh(&ct->lock); + this_way = &seqadj->seq[dir]; + if (this_way->offset_before == this_way->offset_after || + before(this_way->correction_pos, seq)) { + this_way->correction_pos = seq; + this_way->offset_before = this_way->offset_after; + this_way->offset_after += off; + } + spin_unlock_bh(&ct->lock); + return 0; +} +EXPORT_SYMBOL_GPL(nf_ct_seqadj_set); + +void nf_ct_tcp_seqadj_set(struct sk_buff *skb, + struct nf_conn *ct, enum ip_conntrack_info ctinfo, + s32 off) +{ + const struct tcphdr *th; + + if (nf_ct_protonum(ct) != IPPROTO_TCP) + return; + + th = (struct tcphdr *)(skb_network_header(skb) + ip_hdrlen(skb)); + nf_ct_seqadj_set(ct, ctinfo, th->seq, off); +} +EXPORT_SYMBOL_GPL(nf_ct_tcp_seqadj_set); + +/* Adjust one found SACK option including checksum correction */ +static void nf_ct_sack_block_adjust(struct sk_buff *skb, + struct tcphdr *tcph, + unsigned int sackoff, + unsigned int sackend, + struct nf_ct_seqadj *seq) +{ + while (sackoff < sackend) { + struct tcp_sack_block_wire *sack; + __be32 new_start_seq, new_end_seq; + + sack = (void *)skb->data + sackoff; + if (after(ntohl(sack->start_seq) - seq->offset_before, + seq->correction_pos)) + new_start_seq = htonl(ntohl(sack->start_seq) - + seq->offset_after); + else + new_start_seq = htonl(ntohl(sack->start_seq) - + seq->offset_before); + + if (after(ntohl(sack->end_seq) - seq->offset_before, + seq->correction_pos)) + new_end_seq = htonl(ntohl(sack->end_seq) - + seq->offset_after); + else + new_end_seq = htonl(ntohl(sack->end_seq) - + seq->offset_before); + + pr_debug("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n", + ntohl(sack->start_seq), new_start_seq, + ntohl(sack->end_seq), new_end_seq); + + inet_proto_csum_replace4(&tcph->check, skb, + sack->start_seq, new_start_seq, 0); + inet_proto_csum_replace4(&tcph->check, skb, + sack->end_seq, new_end_seq, 0); + sack->start_seq = new_start_seq; + sack->end_seq = new_end_seq; + sackoff += sizeof(*sack); + } +} + +/* TCP SACK sequence number adjustment */ +static unsigned int nf_ct_sack_adjust(struct sk_buff *skb, + unsigned int protoff, + struct tcphdr *tcph, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo) +{ + unsigned int dir, optoff, optend; + struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); + + optoff = protoff + sizeof(struct tcphdr); + optend = protoff + tcph->doff * 4; + + if (!skb_make_writable(skb, optend)) + return 0; + + dir = CTINFO2DIR(ctinfo); + + while (optoff < optend) { + /* Usually: option, length. */ + unsigned char *op = skb->data + optoff; + + switch (op[0]) { + case TCPOPT_EOL: + return 1; + case TCPOPT_NOP: + optoff++; + continue; + default: + /* no partial options */ + if (optoff + 1 == optend || + optoff + op[1] > optend || + op[1] < 2) + return 0; + if (op[0] == TCPOPT_SACK && + op[1] >= 2+TCPOLEN_SACK_PERBLOCK && + ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0) + nf_ct_sack_block_adjust(skb, tcph, optoff + 2, + optoff+op[1], + &seqadj->seq[!dir]); + optoff += op[1]; + } + } + return 1; +} + +/* TCP sequence number adjustment. Returns 1 on success, 0 on failure */ +int nf_ct_seq_adjust(struct sk_buff *skb, + struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int protoff) +{ + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct tcphdr *tcph; + __be32 newseq, newack; + s32 seqoff, ackoff; + struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); + struct nf_ct_seqadj *this_way, *other_way; + int res; + + this_way = &seqadj->seq[dir]; + other_way = &seqadj->seq[!dir]; + + if (!skb_make_writable(skb, protoff + sizeof(*tcph))) + return 0; + + tcph = (void *)skb->data + protoff; + spin_lock_bh(&ct->lock); + if (after(ntohl(tcph->seq), this_way->correction_pos)) + seqoff = this_way->offset_after; + else + seqoff = this_way->offset_before; + + if (after(ntohl(tcph->ack_seq) - other_way->offset_before, + other_way->correction_pos)) + ackoff = other_way->offset_after; + else + ackoff = other_way->offset_before; + + newseq = htonl(ntohl(tcph->seq) + seqoff); + newack = htonl(ntohl(tcph->ack_seq) - ackoff); + + inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0); + inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0); + + pr_debug("Adjusting sequence number from %u->%u, ack from %u->%u\n", + ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq), + ntohl(newack)); + + tcph->seq = newseq; + tcph->ack_seq = newack; + + res = nf_ct_sack_adjust(skb, protoff, tcph, ct, ctinfo); + spin_unlock_bh(&ct->lock); + + return res; +} +EXPORT_SYMBOL_GPL(nf_ct_seq_adjust); + +s32 nf_ct_seq_offset(const struct nf_conn *ct, + enum ip_conntrack_dir dir, + u32 seq) +{ + struct nf_conn_seqadj *seqadj = nfct_seqadj(ct); + struct nf_ct_seqadj *this_way; + + if (!seqadj) + return 0; + + this_way = &seqadj->seq[dir]; + return after(seq, this_way->correction_pos) ? + this_way->offset_after : this_way->offset_before; +} +EXPORT_SYMBOL_GPL(nf_ct_seq_offset); + +static struct nf_ct_ext_type nf_ct_seqadj_extend __read_mostly = { + .len = sizeof(struct nf_conn_seqadj), + .align = __alignof__(struct nf_conn_seqadj), + .id = NF_CT_EXT_SEQADJ, +}; + +int nf_conntrack_seqadj_init(void) +{ + return nf_ct_extend_register(&nf_ct_seqadj_extend); +} + +void nf_conntrack_seqadj_fini(void) +{ + nf_ct_extend_unregister(&nf_ct_seqadj_extend); +} diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 6ff808375b5e..6f0f4f7f68a5 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -402,6 +403,9 @@ nf_nat_setup_info(struct nf_conn *ct, ct->status |= IPS_SRC_NAT; else ct->status |= IPS_DST_NAT; + + if (nfct_help(ct)) + nfct_seqadj_ext_add(ct); } if (maniptype == NF_NAT_MANIP_SRC) { @@ -764,10 +768,6 @@ static struct nf_ct_helper_expectfn follow_master_nat = { .expectfn = nf_nat_follow_master, }; -static struct nfq_ct_nat_hook nfq_ct_nat = { - .seq_adjust = nf_nat_tcp_seq_adjust, -}; - static int __init nf_nat_init(void) { int ret; @@ -787,14 +787,9 @@ static int __init nf_nat_init(void) /* Initialize fake conntrack so that NAT will skip it */ nf_ct_untracked_status_or(IPS_NAT_DONE_MASK); - BUG_ON(nf_nat_seq_adjust_hook != NULL); - RCU_INIT_POINTER(nf_nat_seq_adjust_hook, nf_nat_seq_adjust); BUG_ON(nfnetlink_parse_nat_setup_hook != NULL); RCU_INIT_POINTER(nfnetlink_parse_nat_setup_hook, nfnetlink_parse_nat_setup); - BUG_ON(nf_ct_nat_offset != NULL); - RCU_INIT_POINTER(nf_ct_nat_offset, nf_nat_get_offset); - RCU_INIT_POINTER(nfq_ct_nat_hook, &nfq_ct_nat); #ifdef CONFIG_XFRM BUG_ON(nf_nat_decode_session_hook != NULL); RCU_INIT_POINTER(nf_nat_decode_session_hook, __nf_nat_decode_session); @@ -813,10 +808,7 @@ static void __exit nf_nat_cleanup(void) unregister_pernet_subsys(&nf_nat_net_ops); nf_ct_extend_unregister(&nat_extend); nf_ct_helper_expectfn_unregister(&follow_master_nat); - RCU_INIT_POINTER(nf_nat_seq_adjust_hook, NULL); RCU_INIT_POINTER(nfnetlink_parse_nat_setup_hook, NULL); - RCU_INIT_POINTER(nf_ct_nat_offset, NULL); - RCU_INIT_POINTER(nfq_ct_nat_hook, NULL); #ifdef CONFIG_XFRM RCU_INIT_POINTER(nf_nat_decode_session_hook, NULL); #endif diff --git a/net/netfilter/nf_nat_helper.c b/net/netfilter/nf_nat_helper.c index 46b9baa845a6..2840abb5bb99 100644 --- a/net/netfilter/nf_nat_helper.c +++ b/net/netfilter/nf_nat_helper.c @@ -20,67 +20,13 @@ #include #include #include +#include #include #include #include #include #include -#define DUMP_OFFSET(x) \ - pr_debug("offset_before=%d, offset_after=%d, correction_pos=%u\n", \ - x->offset_before, x->offset_after, x->correction_pos); - -/* Setup TCP sequence correction given this change at this sequence */ -static inline void -adjust_tcp_sequence(u32 seq, - int sizediff, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo) -{ - enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); - struct nf_conn_nat *nat = nfct_nat(ct); - struct nf_nat_seq *this_way = &nat->seq[dir]; - - pr_debug("adjust_tcp_sequence: seq = %u, sizediff = %d\n", - seq, sizediff); - - pr_debug("adjust_tcp_sequence: Seq_offset before: "); - DUMP_OFFSET(this_way); - - spin_lock_bh(&ct->lock); - - /* SYN adjust. If it's uninitialized, or this is after last - * correction, record it: we don't handle more than one - * adjustment in the window, but do deal with common case of a - * retransmit */ - if (this_way->offset_before == this_way->offset_after || - before(this_way->correction_pos, seq)) { - this_way->correction_pos = seq; - this_way->offset_before = this_way->offset_after; - this_way->offset_after += sizediff; - } - spin_unlock_bh(&ct->lock); - - pr_debug("adjust_tcp_sequence: Seq_offset after: "); - DUMP_OFFSET(this_way); -} - -/* Get the offset value, for conntrack. Caller must have the conntrack locked */ -s32 nf_nat_get_offset(const struct nf_conn *ct, - enum ip_conntrack_dir dir, - u32 seq) -{ - struct nf_conn_nat *nat = nfct_nat(ct); - struct nf_nat_seq *this_way; - - if (!nat) - return 0; - - this_way = &nat->seq[dir]; - return after(seq, this_way->correction_pos) - ? this_way->offset_after : this_way->offset_before; -} - /* Frobs data inside this packet, which is linear. */ static void mangle_contents(struct sk_buff *skb, unsigned int dataoff, @@ -135,30 +81,6 @@ static int enlarge_skb(struct sk_buff *skb, unsigned int extra) return 1; } -void nf_nat_set_seq_adjust(struct nf_conn *ct, enum ip_conntrack_info ctinfo, - __be32 seq, s32 off) -{ - if (!off) - return; - set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); - adjust_tcp_sequence(ntohl(seq), off, ct, ctinfo); - nf_conntrack_event_cache(IPCT_NATSEQADJ, ct); -} -EXPORT_SYMBOL_GPL(nf_nat_set_seq_adjust); - -void nf_nat_tcp_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, - u32 ctinfo, int off) -{ - const struct tcphdr *th; - - if (nf_ct_protonum(ct) != IPPROTO_TCP) - return; - - th = (struct tcphdr *)(skb_network_header(skb)+ ip_hdrlen(skb)); - nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off); -} -EXPORT_SYMBOL_GPL(nf_nat_tcp_seq_adjust); - /* Generic function for mangling variable-length address changes inside * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX * command in FTP). @@ -203,8 +125,8 @@ int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, datalen, oldlen); if (adjust && rep_len != match_len) - nf_nat_set_seq_adjust(ct, ctinfo, tcph->seq, - (int)rep_len - (int)match_len); + nf_ct_seqadj_set(ct, ctinfo, tcph->seq, + (int)rep_len - (int)match_len); return 1; } @@ -264,150 +186,6 @@ nf_nat_mangle_udp_packet(struct sk_buff *skb, } EXPORT_SYMBOL(nf_nat_mangle_udp_packet); -/* Adjust one found SACK option including checksum correction */ -static void -sack_adjust(struct sk_buff *skb, - struct tcphdr *tcph, - unsigned int sackoff, - unsigned int sackend, - struct nf_nat_seq *natseq) -{ - while (sackoff < sackend) { - struct tcp_sack_block_wire *sack; - __be32 new_start_seq, new_end_seq; - - sack = (void *)skb->data + sackoff; - if (after(ntohl(sack->start_seq) - natseq->offset_before, - natseq->correction_pos)) - new_start_seq = htonl(ntohl(sack->start_seq) - - natseq->offset_after); - else - new_start_seq = htonl(ntohl(sack->start_seq) - - natseq->offset_before); - - if (after(ntohl(sack->end_seq) - natseq->offset_before, - natseq->correction_pos)) - new_end_seq = htonl(ntohl(sack->end_seq) - - natseq->offset_after); - else - new_end_seq = htonl(ntohl(sack->end_seq) - - natseq->offset_before); - - pr_debug("sack_adjust: start_seq: %d->%d, end_seq: %d->%d\n", - ntohl(sack->start_seq), new_start_seq, - ntohl(sack->end_seq), new_end_seq); - - inet_proto_csum_replace4(&tcph->check, skb, - sack->start_seq, new_start_seq, 0); - inet_proto_csum_replace4(&tcph->check, skb, - sack->end_seq, new_end_seq, 0); - sack->start_seq = new_start_seq; - sack->end_seq = new_end_seq; - sackoff += sizeof(*sack); - } -} - -/* TCP SACK sequence number adjustment */ -static inline unsigned int -nf_nat_sack_adjust(struct sk_buff *skb, - unsigned int protoff, - struct tcphdr *tcph, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo) -{ - unsigned int dir, optoff, optend; - struct nf_conn_nat *nat = nfct_nat(ct); - - optoff = protoff + sizeof(struct tcphdr); - optend = protoff + tcph->doff * 4; - - if (!skb_make_writable(skb, optend)) - return 0; - - dir = CTINFO2DIR(ctinfo); - - while (optoff < optend) { - /* Usually: option, length. */ - unsigned char *op = skb->data + optoff; - - switch (op[0]) { - case TCPOPT_EOL: - return 1; - case TCPOPT_NOP: - optoff++; - continue; - default: - /* no partial options */ - if (optoff + 1 == optend || - optoff + op[1] > optend || - op[1] < 2) - return 0; - if (op[0] == TCPOPT_SACK && - op[1] >= 2+TCPOLEN_SACK_PERBLOCK && - ((op[1] - 2) % TCPOLEN_SACK_PERBLOCK) == 0) - sack_adjust(skb, tcph, optoff+2, - optoff+op[1], &nat->seq[!dir]); - optoff += op[1]; - } - } - return 1; -} - -/* TCP sequence number adjustment. Returns 1 on success, 0 on failure */ -int -nf_nat_seq_adjust(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff) -{ - struct tcphdr *tcph; - int dir; - __be32 newseq, newack; - s32 seqoff, ackoff; - struct nf_conn_nat *nat = nfct_nat(ct); - struct nf_nat_seq *this_way, *other_way; - int res; - - dir = CTINFO2DIR(ctinfo); - - this_way = &nat->seq[dir]; - other_way = &nat->seq[!dir]; - - if (!skb_make_writable(skb, protoff + sizeof(*tcph))) - return 0; - - tcph = (void *)skb->data + protoff; - spin_lock_bh(&ct->lock); - if (after(ntohl(tcph->seq), this_way->correction_pos)) - seqoff = this_way->offset_after; - else - seqoff = this_way->offset_before; - - if (after(ntohl(tcph->ack_seq) - other_way->offset_before, - other_way->correction_pos)) - ackoff = other_way->offset_after; - else - ackoff = other_way->offset_before; - - newseq = htonl(ntohl(tcph->seq) + seqoff); - newack = htonl(ntohl(tcph->ack_seq) - ackoff); - - inet_proto_csum_replace4(&tcph->check, skb, tcph->seq, newseq, 0); - inet_proto_csum_replace4(&tcph->check, skb, tcph->ack_seq, newack, 0); - - pr_debug("Adjusting sequence number from %u->%u, ack from %u->%u\n", - ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq), - ntohl(newack)); - - tcph->seq = newseq; - tcph->ack_seq = newack; - - res = nf_nat_sack_adjust(skb, protoff, tcph, ct, ctinfo); - spin_unlock_bh(&ct->lock); - - return res; -} - /* Setup NAT on this expected conntrack so it follows master. */ /* If we fail to get a free NAT slot, we'll get dropped on confirm */ void nf_nat_follow_master(struct nf_conn *ct, diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index dac11f73868e..f9790405b7ff 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -20,6 +20,7 @@ #include #include #include +#include #include MODULE_LICENSE("GPL"); @@ -308,7 +309,7 @@ static void nf_nat_sip_seq_adjust(struct sk_buff *skb, unsigned int protoff, return; th = (struct tcphdr *)(skb->data + protoff); - nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off); + nf_ct_seqadj_set(ct, ctinfo, th->seq, off); } /* Handles expected signalling connections and media streams */ diff --git a/net/netfilter/nfnetlink_queue_ct.c b/net/netfilter/nfnetlink_queue_ct.c index be893039966d..96cac50e0d12 100644 --- a/net/netfilter/nfnetlink_queue_ct.c +++ b/net/netfilter/nfnetlink_queue_ct.c @@ -87,14 +87,14 @@ nla_put_failure: void nfqnl_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, enum ip_conntrack_info ctinfo, int diff) { - struct nfq_ct_nat_hook *nfq_nat_ct; + struct nfq_ct_hook *nfq_ct; - nfq_nat_ct = rcu_dereference(nfq_ct_nat_hook); - if (nfq_nat_ct == NULL) + nfq_ct = rcu_dereference(nfq_ct_hook); + if (nfq_ct == NULL) return; if ((ct->status & IPS_NAT_MASK) && diff) - nfq_nat_ct->seq_adjust(skb, ct, ctinfo, diff); + nfq_ct->seq_adjust(skb, ct, ctinfo, diff); } int nfqnl_attach_expect(struct nf_conn *ct, const struct nlattr *attr, -- cgit v1.2.3-71-gd317 From 0198230b7705eb2386e53778d944e307eef0cc71 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 27 Aug 2013 08:50:13 +0200 Subject: net: syncookies: export cookie_v4_init_sequence/cookie_v4_check Extract the local TCP stack independant parts of tcp_v4_init_sequence() and cookie_v4_check() and export them for use by the upcoming SYNPROXY target. Signed-off-by: Patrick McHardy Acked-by: David S. Miller Tested-by: Martin Topholm Signed-off-by: Jesper Dangaard Brouer Signed-off-by: Pablo Neira Ayuso --- include/net/tcp.h | 4 ++++ net/ipv4/syncookies.c | 29 ++++++++++++++++++----------- 2 files changed, 22 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 09cb5c11ceea..8e2b63678a83 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -476,9 +476,13 @@ void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb); /* From syncookies.c */ extern __u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS]; +extern int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th, + u32 cookie); extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, struct ip_options *opt); #ifdef CONFIG_SYN_COOKIES +extern u32 __cookie_v4_init_sequence(const struct iphdr *iph, + const struct tcphdr *th, u16 *mssp); extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mss); #else diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index b05c96e7af8b..14a15c49129d 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -160,26 +160,33 @@ static __u16 const msstab[] = { * Generate a syncookie. mssp points to the mss, which is returned * rounded down to the value encoded in the cookie. */ -__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) +u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th, + u16 *mssp) { - const struct iphdr *iph = ip_hdr(skb); - const struct tcphdr *th = tcp_hdr(skb); int mssind; const __u16 mss = *mssp; - tcp_synq_overflow(sk); - for (mssind = ARRAY_SIZE(msstab) - 1; mssind ; mssind--) if (mss >= msstab[mssind]) break; *mssp = msstab[mssind]; - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); - return secure_tcp_syn_cookie(iph->saddr, iph->daddr, th->source, th->dest, ntohl(th->seq), jiffies / (HZ * 60), mssind); } +EXPORT_SYMBOL_GPL(__cookie_v4_init_sequence); + +__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) +{ + const struct iphdr *iph = ip_hdr(skb); + const struct tcphdr *th = tcp_hdr(skb); + + tcp_synq_overflow(sk); + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); + + return __cookie_v4_init_sequence(iph, th, mssp); +} /* * This (misnamed) value is the age of syncookie which is permitted. @@ -192,10 +199,9 @@ __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) * Check if a ack sequence number is a valid syncookie. * Return the decoded mss if it is, or 0 if not. */ -static inline int cookie_check(struct sk_buff *skb, __u32 cookie) +int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th, + u32 cookie) { - const struct iphdr *iph = ip_hdr(skb); - const struct tcphdr *th = tcp_hdr(skb); __u32 seq = ntohl(th->seq) - 1; __u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr, th->source, th->dest, seq, @@ -204,6 +210,7 @@ static inline int cookie_check(struct sk_buff *skb, __u32 cookie) return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0; } +EXPORT_SYMBOL_GPL(__cookie_v4_check); static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, struct request_sock *req, @@ -284,7 +291,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, goto out; if (tcp_synq_no_recent_overflow(sk) || - (mss = cookie_check(skb, cookie)) == 0) { + (mss = __cookie_v4_check(ip_hdr(skb), th, cookie)) == 0) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED); goto out; } -- cgit v1.2.3-71-gd317 From 48b1de4c110a7afa4b85862f6c75af817db26fad Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 27 Aug 2013 08:50:14 +0200 Subject: netfilter: add SYNPROXY core/target Add a SYNPROXY for netfilter. The code is split into two parts, the synproxy core with common functions and an address family specific target. The SYNPROXY receives the connection request from the client, responds with a SYN/ACK containing a SYN cookie and announcing a zero window and checks whether the final ACK from the client contains a valid cookie. It then establishes a connection to the original destination and, if successful, sends a window update to the client with the window size announced by the server. Support for timestamps, SACK, window scaling and MSS options can be statically configured as target parameters if the features of the server are known. If timestamps are used, the timestamp value sent back to the client in the SYN/ACK will be different from the real timestamp of the server. In order to now break PAWS, the timestamps are translated in the direction server->client. Signed-off-by: Patrick McHardy Tested-by: Martin Topholm Signed-off-by: Jesper Dangaard Brouer Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_extend.h | 6 +- include/net/netfilter/nf_conntrack_seqadj.h | 2 + include/net/netfilter/nf_conntrack_synproxy.h | 77 +++++ include/uapi/linux/netfilter/xt_SYNPROXY.h | 16 + net/ipv4/netfilter/Kconfig | 13 + net/ipv4/netfilter/Makefile | 1 + net/ipv4/netfilter/ipt_SYNPROXY.c | 472 ++++++++++++++++++++++++++ net/netfilter/Kconfig | 3 + net/netfilter/Makefile | 3 + net/netfilter/nf_conntrack_core.c | 6 + net/netfilter/nf_conntrack_proto_tcp.c | 16 + net/netfilter/nf_conntrack_seqadj.c | 20 ++ net/netfilter/nf_synproxy_core.c | 432 +++++++++++++++++++++++ 13 files changed, 1066 insertions(+), 1 deletion(-) create mode 100644 include/net/netfilter/nf_conntrack_synproxy.h create mode 100644 include/uapi/linux/netfilter/xt_SYNPROXY.h create mode 100644 net/ipv4/netfilter/ipt_SYNPROXY.c create mode 100644 net/netfilter/nf_synproxy_core.c (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 2a22bcbfe6e4..ff95434e50ca 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -9,8 +9,8 @@ enum nf_ct_ext_id { NF_CT_EXT_HELPER, #if defined(CONFIG_NF_NAT) || defined(CONFIG_NF_NAT_MODULE) NF_CT_EXT_NAT, - NF_CT_EXT_SEQADJ, #endif + NF_CT_EXT_SEQADJ, NF_CT_EXT_ACCT, #ifdef CONFIG_NF_CONNTRACK_EVENTS NF_CT_EXT_ECACHE, @@ -26,6 +26,9 @@ enum nf_ct_ext_id { #endif #ifdef CONFIG_NF_CONNTRACK_LABELS NF_CT_EXT_LABELS, +#endif +#if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY) + NF_CT_EXT_SYNPROXY, #endif NF_CT_EXT_NUM, }; @@ -39,6 +42,7 @@ enum nf_ct_ext_id { #define NF_CT_EXT_TSTAMP_TYPE struct nf_conn_tstamp #define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout #define NF_CT_EXT_LABELS_TYPE struct nf_conn_labels +#define NF_CT_EXT_SYNPROXY_TYPE struct nf_conn_synproxy /* Extensions: optional stuff which isn't permanently in struct. */ struct nf_ct_ext { diff --git a/include/net/netfilter/nf_conntrack_seqadj.h b/include/net/netfilter/nf_conntrack_seqadj.h index 30bfbbed9f47..f6177a5fe0ca 100644 --- a/include/net/netfilter/nf_conntrack_seqadj.h +++ b/include/net/netfilter/nf_conntrack_seqadj.h @@ -30,6 +30,8 @@ static inline struct nf_conn_seqadj *nfct_seqadj_ext_add(struct nf_conn *ct) return nf_ct_ext_add(ct, NF_CT_EXT_SEQADJ, GFP_ATOMIC); } +extern int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo, + s32 off); extern int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo, __be32 seq, s32 off); extern void nf_ct_tcp_seqadj_set(struct sk_buff *skb, diff --git a/include/net/netfilter/nf_conntrack_synproxy.h b/include/net/netfilter/nf_conntrack_synproxy.h new file mode 100644 index 000000000000..806f54a290d6 --- /dev/null +++ b/include/net/netfilter/nf_conntrack_synproxy.h @@ -0,0 +1,77 @@ +#ifndef _NF_CONNTRACK_SYNPROXY_H +#define _NF_CONNTRACK_SYNPROXY_H + +#include + +struct nf_conn_synproxy { + u32 isn; + u32 its; + u32 tsoff; +}; + +static inline struct nf_conn_synproxy *nfct_synproxy(const struct nf_conn *ct) +{ +#if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY) + return nf_ct_ext_find(ct, NF_CT_EXT_SYNPROXY); +#else + return NULL; +#endif +} + +static inline struct nf_conn_synproxy *nfct_synproxy_ext_add(struct nf_conn *ct) +{ +#if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY) + return nf_ct_ext_add(ct, NF_CT_EXT_SYNPROXY, GFP_ATOMIC); +#else + return NULL; +#endif +} + +struct synproxy_stats { + unsigned int syn_received; + unsigned int cookie_invalid; + unsigned int cookie_valid; + unsigned int cookie_retrans; + unsigned int conn_reopened; +}; + +struct synproxy_net { + struct nf_conn *tmpl; + struct synproxy_stats __percpu *stats; +}; + +extern int synproxy_net_id; +static inline struct synproxy_net *synproxy_pernet(struct net *net) +{ + return net_generic(net, synproxy_net_id); +} + +struct synproxy_options { + u8 options; + u8 wscale; + u16 mss; + u32 tsval; + u32 tsecr; +}; + +struct tcphdr; +struct xt_synproxy_info; +extern void synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, + const struct tcphdr *th, + struct synproxy_options *opts); +extern unsigned int synproxy_options_size(const struct synproxy_options *opts); +extern void synproxy_build_options(struct tcphdr *th, + const struct synproxy_options *opts); + +extern void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info, + struct synproxy_options *opts); +extern void synproxy_check_timestamp_cookie(struct synproxy_options *opts); + +extern unsigned int synproxy_tstamp_adjust(struct sk_buff *skb, + unsigned int protoff, + struct tcphdr *th, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + const struct nf_conn_synproxy *synproxy); + +#endif /* _NF_CONNTRACK_SYNPROXY_H */ diff --git a/include/uapi/linux/netfilter/xt_SYNPROXY.h b/include/uapi/linux/netfilter/xt_SYNPROXY.h new file mode 100644 index 000000000000..2d59fbaa93c6 --- /dev/null +++ b/include/uapi/linux/netfilter/xt_SYNPROXY.h @@ -0,0 +1,16 @@ +#ifndef _XT_SYNPROXY_H +#define _XT_SYNPROXY_H + +#define XT_SYNPROXY_OPT_MSS 0x01 +#define XT_SYNPROXY_OPT_WSCALE 0x02 +#define XT_SYNPROXY_OPT_SACK_PERM 0x04 +#define XT_SYNPROXY_OPT_TIMESTAMP 0x08 +#define XT_SYNPROXY_OPT_ECN 0x10 + +struct xt_synproxy_info { + __u8 options; + __u8 wscale; + __u16 mss; +}; + +#endif /* _XT_SYNPROXY_H */ diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 4e9028017428..1657e39b291f 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -110,6 +110,19 @@ config IP_NF_TARGET_REJECT To compile it as a module, choose M here. If unsure, say N. +config IP_NF_TARGET_SYNPROXY + tristate "SYNPROXY target support" + depends on NF_CONNTRACK && NETFILTER_ADVANCED + select NETFILTER_SYNPROXY + select SYN_COOKIES + help + The SYNPROXY target allows you to intercept TCP connections and + establish them using syncookies before they are passed on to the + server. This allows to avoid conntrack and server resource usage + during SYN-flood attacks. + + To compile it as a module, choose M here. If unsure, say N. + config IP_NF_TARGET_ULOG tristate "ULOG target support (obsolete)" default m if NETFILTER_ADVANCED=n diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 007b128eecc9..3622b248b6dd 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o +obj-$(CONFIG_IP_NF_TARGET_SYNPROXY) += ipt_SYNPROXY.o obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o # generic ARP tables diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c new file mode 100644 index 000000000000..94371db6aecc --- /dev/null +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2013 Patrick McHardy + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +static struct iphdr * +synproxy_build_ip(struct sk_buff *skb, u32 saddr, u32 daddr) +{ + struct iphdr *iph; + + skb_reset_network_header(skb); + iph = (struct iphdr *)skb_put(skb, sizeof(*iph)); + iph->version = 4; + iph->ihl = sizeof(*iph) / 4; + iph->tos = 0; + iph->id = 0; + iph->frag_off = htons(IP_DF); + iph->ttl = sysctl_ip_default_ttl; + iph->protocol = IPPROTO_TCP; + iph->check = 0; + iph->saddr = saddr; + iph->daddr = daddr; + + return iph; +} + +static void +synproxy_send_tcp(const struct sk_buff *skb, struct sk_buff *nskb, + struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo, + struct iphdr *niph, struct tcphdr *nth, + unsigned int tcp_hdr_size) +{ + nth->check = ~tcp_v4_check(tcp_hdr_size, niph->saddr, niph->daddr, 0); + nskb->ip_summed = CHECKSUM_PARTIAL; + nskb->csum_start = (unsigned char *)nth - nskb->head; + nskb->csum_offset = offsetof(struct tcphdr, check); + + skb_dst_set_noref(nskb, skb_dst(skb)); + nskb->protocol = htons(ETH_P_IP); + if (ip_route_me_harder(nskb, RTN_UNSPEC)) + goto free_nskb; + + if (nfct) { + nskb->nfct = nfct; + nskb->nfctinfo = ctinfo; + nf_conntrack_get(nfct); + } + + ip_local_out(nskb); + return; + +free_nskb: + kfree_skb(nskb); +} + +static void +synproxy_send_client_synack(const struct sk_buff *skb, const struct tcphdr *th, + const struct synproxy_options *opts) +{ + struct sk_buff *nskb; + struct iphdr *iph, *niph; + struct tcphdr *nth; + unsigned int tcp_hdr_size; + u16 mss = opts->mss; + + iph = ip_hdr(skb); + + tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); + nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, + GFP_ATOMIC); + if (nskb == NULL) + return; + skb_reserve(nskb, MAX_TCP_HEADER); + + niph = synproxy_build_ip(nskb, iph->daddr, iph->saddr); + + skb_reset_transport_header(nskb); + nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size); + nth->source = th->dest; + nth->dest = th->source; + nth->seq = htonl(__cookie_v4_init_sequence(iph, th, &mss)); + nth->ack_seq = htonl(ntohl(th->seq) + 1); + tcp_flag_word(nth) = TCP_FLAG_SYN | TCP_FLAG_ACK; + if (opts->options & XT_SYNPROXY_OPT_ECN) + tcp_flag_word(nth) |= TCP_FLAG_ECE; + nth->doff = tcp_hdr_size / 4; + nth->window = 0; + nth->check = 0; + nth->urg_ptr = 0; + + synproxy_build_options(nth, opts); + + synproxy_send_tcp(skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY, + niph, nth, tcp_hdr_size); +} + +static void +synproxy_send_server_syn(const struct synproxy_net *snet, + const struct sk_buff *skb, const struct tcphdr *th, + const struct synproxy_options *opts, u32 recv_seq) +{ + struct sk_buff *nskb; + struct iphdr *iph, *niph; + struct tcphdr *nth; + unsigned int tcp_hdr_size; + + iph = ip_hdr(skb); + + tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); + nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, + GFP_ATOMIC); + if (nskb == NULL) + return; + skb_reserve(nskb, MAX_TCP_HEADER); + + niph = synproxy_build_ip(nskb, iph->saddr, iph->daddr); + + skb_reset_transport_header(nskb); + nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size); + nth->source = th->source; + nth->dest = th->dest; + nth->seq = htonl(recv_seq - 1); + /* ack_seq is used to relay our ISN to the synproxy hook to initialize + * sequence number translation once a connection tracking entry exists. + */ + nth->ack_seq = htonl(ntohl(th->ack_seq) - 1); + tcp_flag_word(nth) = TCP_FLAG_SYN; + if (opts->options & XT_SYNPROXY_OPT_ECN) + tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR; + nth->doff = tcp_hdr_size / 4; + nth->window = th->window; + nth->check = 0; + nth->urg_ptr = 0; + + synproxy_build_options(nth, opts); + + synproxy_send_tcp(skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW, + niph, nth, tcp_hdr_size); +} + +static void +synproxy_send_server_ack(const struct synproxy_net *snet, + const struct ip_ct_tcp *state, + const struct sk_buff *skb, const struct tcphdr *th, + const struct synproxy_options *opts) +{ + struct sk_buff *nskb; + struct iphdr *iph, *niph; + struct tcphdr *nth; + unsigned int tcp_hdr_size; + + iph = ip_hdr(skb); + + tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); + nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, + GFP_ATOMIC); + if (nskb == NULL) + return; + skb_reserve(nskb, MAX_TCP_HEADER); + + niph = synproxy_build_ip(nskb, iph->daddr, iph->saddr); + + skb_reset_transport_header(nskb); + nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size); + nth->source = th->dest; + nth->dest = th->source; + nth->seq = htonl(ntohl(th->ack_seq)); + nth->ack_seq = htonl(ntohl(th->seq) + 1); + tcp_flag_word(nth) = TCP_FLAG_ACK; + nth->doff = tcp_hdr_size / 4; + nth->window = htons(state->seen[IP_CT_DIR_ORIGINAL].td_maxwin); + nth->check = 0; + nth->urg_ptr = 0; + + synproxy_build_options(nth, opts); + + synproxy_send_tcp(skb, nskb, NULL, 0, niph, nth, tcp_hdr_size); +} + +static void +synproxy_send_client_ack(const struct synproxy_net *snet, + const struct sk_buff *skb, const struct tcphdr *th, + const struct synproxy_options *opts) +{ + struct sk_buff *nskb; + struct iphdr *iph, *niph; + struct tcphdr *nth; + unsigned int tcp_hdr_size; + + iph = ip_hdr(skb); + + tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts); + nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER, + GFP_ATOMIC); + if (nskb == NULL) + return; + skb_reserve(nskb, MAX_TCP_HEADER); + + niph = synproxy_build_ip(nskb, iph->saddr, iph->daddr); + + skb_reset_transport_header(nskb); + nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size); + nth->source = th->source; + nth->dest = th->dest; + nth->seq = htonl(ntohl(th->seq) + 1); + nth->ack_seq = th->ack_seq; + tcp_flag_word(nth) = TCP_FLAG_ACK; + nth->doff = tcp_hdr_size / 4; + nth->window = ntohs(htons(th->window) >> opts->wscale); + nth->check = 0; + nth->urg_ptr = 0; + + synproxy_build_options(nth, opts); + + synproxy_send_tcp(skb, nskb, NULL, 0, niph, nth, tcp_hdr_size); +} + +static bool +synproxy_recv_client_ack(const struct synproxy_net *snet, + const struct sk_buff *skb, const struct tcphdr *th, + struct synproxy_options *opts, u32 recv_seq) +{ + int mss; + + mss = __cookie_v4_check(ip_hdr(skb), th, ntohl(th->ack_seq) - 1); + if (mss == 0) { + this_cpu_inc(snet->stats->cookie_invalid); + return false; + } + + this_cpu_inc(snet->stats->cookie_valid); + opts->mss = mss; + + if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP) + synproxy_check_timestamp_cookie(opts); + + synproxy_send_server_syn(snet, skb, th, opts, recv_seq); + return true; +} + +static unsigned int +synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_synproxy_info *info = par->targinfo; + struct synproxy_net *snet = synproxy_pernet(dev_net(par->in)); + struct synproxy_options opts = {}; + struct tcphdr *th, _th; + + if (nf_ip_checksum(skb, par->hooknum, par->thoff, IPPROTO_TCP)) + return NF_DROP; + + th = skb_header_pointer(skb, par->thoff, sizeof(_th), &_th); + if (th == NULL) + return NF_DROP; + + synproxy_parse_options(skb, par->thoff, th, &opts); + + if (th->syn && !th->ack) { + /* Initial SYN from client */ + this_cpu_inc(snet->stats->syn_received); + + if (th->ece && th->cwr) + opts.options |= XT_SYNPROXY_OPT_ECN; + + opts.options &= info->options; + if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) + synproxy_init_timestamp_cookie(info, &opts); + else + opts.options &= ~(XT_SYNPROXY_OPT_WSCALE | + XT_SYNPROXY_OPT_SACK_PERM | + XT_SYNPROXY_OPT_ECN); + + synproxy_send_client_synack(skb, th, &opts); + } else if (th->ack && !(th->fin || th->rst)) + /* ACK from client */ + synproxy_recv_client_ack(snet, skb, th, &opts, ntohl(th->seq)); + + return NF_DROP; +} + +static unsigned int ipv4_synproxy_hook(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct synproxy_net *snet = synproxy_pernet(dev_net(in ? : out)); + enum ip_conntrack_info ctinfo; + struct nf_conn *ct; + struct nf_conn_synproxy *synproxy; + struct synproxy_options opts = {}; + const struct ip_ct_tcp *state; + struct tcphdr *th, _th; + unsigned int thoff; + + ct = nf_ct_get(skb, &ctinfo); + if (ct == NULL) + return NF_ACCEPT; + + synproxy = nfct_synproxy(ct); + if (synproxy == NULL) + return NF_ACCEPT; + + if (nf_is_loopback_packet(skb)) + return NF_ACCEPT; + + thoff = ip_hdrlen(skb); + th = skb_header_pointer(skb, thoff, sizeof(_th), &_th); + if (th == NULL) + return NF_DROP; + + state = &ct->proto.tcp; + switch (state->state) { + case TCP_CONNTRACK_CLOSE: + if (th->rst && !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) { + nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - + ntohl(th->seq) + 1); + break; + } + + if (!th->syn || th->ack || + CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) + break; + + /* Reopened connection - reset the sequence number and timestamp + * adjustments, they will get initialized once the connection is + * reestablished. + */ + nf_ct_seqadj_init(ct, ctinfo, 0); + synproxy->tsoff = 0; + this_cpu_inc(snet->stats->conn_reopened); + + /* fall through */ + case TCP_CONNTRACK_SYN_SENT: + synproxy_parse_options(skb, thoff, th, &opts); + + if (!th->syn && th->ack && + CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) { + /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1, + * therefore we need to add 1 to make the SYN sequence + * number match the one of first SYN. + */ + if (synproxy_recv_client_ack(snet, skb, th, &opts, + ntohl(th->seq) + 1)) + this_cpu_inc(snet->stats->cookie_retrans); + + return NF_DROP; + } + + synproxy->isn = ntohl(th->ack_seq); + if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) + synproxy->its = opts.tsecr; + break; + case TCP_CONNTRACK_SYN_RECV: + if (!th->syn || !th->ack) + break; + + synproxy_parse_options(skb, thoff, th, &opts); + if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP) + synproxy->tsoff = opts.tsval - synproxy->its; + + opts.options &= ~(XT_SYNPROXY_OPT_MSS | + XT_SYNPROXY_OPT_WSCALE | + XT_SYNPROXY_OPT_SACK_PERM); + + swap(opts.tsval, opts.tsecr); + synproxy_send_server_ack(snet, state, skb, th, &opts); + + nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq)); + + swap(opts.tsval, opts.tsecr); + synproxy_send_client_ack(snet, skb, th, &opts); + + consume_skb(skb); + return NF_STOLEN; + default: + break; + } + + synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy); + return NF_ACCEPT; +} + +static int synproxy_tg4_check(const struct xt_tgchk_param *par) +{ + const struct ipt_entry *e = par->entryinfo; + + if (e->ip.proto != IPPROTO_TCP || + e->ip.invflags & XT_INV_PROTO) + return -EINVAL; + + return nf_ct_l3proto_try_module_get(par->family); +} + +static void synproxy_tg4_destroy(const struct xt_tgdtor_param *par) +{ + nf_ct_l3proto_module_put(par->family); +} + +static struct xt_target synproxy_tg4_reg __read_mostly = { + .name = "SYNPROXY", + .family = NFPROTO_IPV4, + .target = synproxy_tg4, + .targetsize = sizeof(struct xt_synproxy_info), + .checkentry = synproxy_tg4_check, + .destroy = synproxy_tg4_destroy, + .me = THIS_MODULE, +}; + +static struct nf_hook_ops ipv4_synproxy_ops[] __read_mostly = { + { + .hook = ipv4_synproxy_hook, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, + }, + { + .hook = ipv4_synproxy_hook, + .owner = THIS_MODULE, + .pf = NFPROTO_IPV4, + .hooknum = NF_INET_POST_ROUTING, + .priority = NF_IP_PRI_CONNTRACK_CONFIRM - 1, + }, +}; + +static int __init synproxy_tg4_init(void) +{ + int err; + + err = nf_register_hooks(ipv4_synproxy_ops, + ARRAY_SIZE(ipv4_synproxy_ops)); + if (err < 0) + goto err1; + + err = xt_register_target(&synproxy_tg4_reg); + if (err < 0) + goto err2; + + return 0; + +err2: + nf_unregister_hooks(ipv4_synproxy_ops, ARRAY_SIZE(ipv4_synproxy_ops)); +err1: + return err; +} + +static void __exit synproxy_tg4_exit(void) +{ + xt_unregister_target(&synproxy_tg4_reg); + nf_unregister_hooks(ipv4_synproxy_ops, ARRAY_SIZE(ipv4_synproxy_ops)); +} + +module_init(synproxy_tg4_init); +module_exit(synproxy_tg4_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index c45fc1a60e0d..62a171ab204f 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -408,6 +408,9 @@ config NF_NAT_TFTP depends on NF_CONNTRACK && NF_NAT default NF_NAT && NF_CONNTRACK_TFTP +config NETFILTER_SYNPROXY + tristate + endif # NF_CONNTRACK config NETFILTER_XTABLES diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 89a9c1658f5e..c3a0a12907f6 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -61,6 +61,9 @@ obj-$(CONFIG_NF_NAT_IRC) += nf_nat_irc.o obj-$(CONFIG_NF_NAT_SIP) += nf_nat_sip.o obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o +# SYNPROXY +obj-$(CONFIG_NETFILTER_SYNPROXY) += nf_synproxy_core.o + # generic X tables obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 00a7a94d4132..5d892febd64c 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -799,6 +800,11 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, if (IS_ERR(ct)) return (struct nf_conntrack_tuple_hash *)ct; + if (tmpl && nfct_synproxy(tmpl)) { + nfct_seqadj_ext_add(ct); + nfct_synproxy_ext_add(ct); + } + timeout_ext = tmpl ? nf_ct_timeout_find(tmpl) : NULL; if (timeout_ext) timeouts = NF_CT_TIMEOUT_EXT_DATA(timeout_ext); diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 984a8d1a3359..44d1ea32570a 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -946,6 +947,21 @@ static int tcp_packet(struct nf_conn *ct, "state %s ", tcp_conntrack_names[old_state]); return NF_ACCEPT; case TCP_CONNTRACK_MAX: + /* Special case for SYN proxy: when the SYN to the server or + * the SYN/ACK from the server is lost, the client may transmit + * a keep-alive packet while in SYN_SENT state. This needs to + * be associated with the original conntrack entry in order to + * generate a new SYN with the correct sequence number. + */ + if (nfct_synproxy(ct) && old_state == TCP_CONNTRACK_SYN_SENT && + index == TCP_ACK_SET && dir == IP_CT_DIR_ORIGINAL && + ct->proto.tcp.last_dir == IP_CT_DIR_ORIGINAL && + ct->proto.tcp.seen[dir].td_end - 1 == ntohl(th->seq)) { + pr_debug("nf_ct_tcp: SYN proxy client keep alive\n"); + spin_unlock_bh(&ct->lock); + return NF_ACCEPT; + } + /* Invalid packet */ pr_debug("nf_ct_tcp: Invalid dir=%i index=%u ostate=%u\n", dir, get_conntrack_index(th), old_state); diff --git a/net/netfilter/nf_conntrack_seqadj.c b/net/netfilter/nf_conntrack_seqadj.c index 483eb9ce3216..5f9bfd060dea 100644 --- a/net/netfilter/nf_conntrack_seqadj.c +++ b/net/netfilter/nf_conntrack_seqadj.c @@ -6,6 +6,26 @@ #include #include +int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo, + s32 off) +{ + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + struct nf_conn_seqadj *seqadj; + struct nf_ct_seqadj *this_way; + + if (off == 0) + return 0; + + set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); + + seqadj = nfct_seqadj(ct); + this_way = &seqadj->seq[dir]; + this_way->offset_before = off; + this_way->offset_after = off; + return 0; +} +EXPORT_SYMBOL_GPL(nf_ct_seqadj_init); + int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo, __be32 seq, s32 off) { diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c new file mode 100644 index 000000000000..d23dc791aca7 --- /dev/null +++ b/net/netfilter/nf_synproxy_core.c @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2013 Patrick McHardy + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +int synproxy_net_id; +EXPORT_SYMBOL_GPL(synproxy_net_id); + +void +synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, + const struct tcphdr *th, struct synproxy_options *opts) +{ + int length = (th->doff * 4) - sizeof(*th); + u8 buf[40], *ptr; + + ptr = skb_header_pointer(skb, doff + sizeof(*th), length, buf); + BUG_ON(ptr == NULL); + + opts->options = 0; + while (length > 0) { + int opcode = *ptr++; + int opsize; + + switch (opcode) { + case TCPOPT_EOL: + return; + case TCPOPT_NOP: + length--; + continue; + default: + opsize = *ptr++; + if (opsize < 2) + return; + if (opsize > length) + return; + + switch (opcode) { + case TCPOPT_MSS: + if (opsize == TCPOLEN_MSS) { + opts->mss = get_unaligned_be16(ptr); + opts->options |= XT_SYNPROXY_OPT_MSS; + } + break; + case TCPOPT_WINDOW: + if (opsize == TCPOLEN_WINDOW) { + opts->wscale = *ptr; + if (opts->wscale > 14) + opts->wscale = 14; + opts->options |= XT_SYNPROXY_OPT_WSCALE; + } + break; + case TCPOPT_TIMESTAMP: + if (opsize == TCPOLEN_TIMESTAMP) { + opts->tsval = get_unaligned_be32(ptr); + opts->tsecr = get_unaligned_be32(ptr + 4); + opts->options |= XT_SYNPROXY_OPT_TIMESTAMP; + } + break; + case TCPOPT_SACK_PERM: + if (opsize == TCPOLEN_SACK_PERM) + opts->options |= XT_SYNPROXY_OPT_SACK_PERM; + break; + } + + ptr += opsize - 2; + length -= opsize; + } + } +} +EXPORT_SYMBOL_GPL(synproxy_parse_options); + +unsigned int synproxy_options_size(const struct synproxy_options *opts) +{ + unsigned int size = 0; + + if (opts->options & XT_SYNPROXY_OPT_MSS) + size += TCPOLEN_MSS_ALIGNED; + if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP) + size += TCPOLEN_TSTAMP_ALIGNED; + else if (opts->options & XT_SYNPROXY_OPT_SACK_PERM) + size += TCPOLEN_SACKPERM_ALIGNED; + if (opts->options & XT_SYNPROXY_OPT_WSCALE) + size += TCPOLEN_WSCALE_ALIGNED; + + return size; +} +EXPORT_SYMBOL_GPL(synproxy_options_size); + +void +synproxy_build_options(struct tcphdr *th, const struct synproxy_options *opts) +{ + __be32 *ptr = (__be32 *)(th + 1); + u8 options = opts->options; + + if (options & XT_SYNPROXY_OPT_MSS) + *ptr++ = htonl((TCPOPT_MSS << 24) | + (TCPOLEN_MSS << 16) | + opts->mss); + + if (options & XT_SYNPROXY_OPT_TIMESTAMP) { + if (options & XT_SYNPROXY_OPT_SACK_PERM) + *ptr++ = htonl((TCPOPT_SACK_PERM << 24) | + (TCPOLEN_SACK_PERM << 16) | + (TCPOPT_TIMESTAMP << 8) | + TCPOLEN_TIMESTAMP); + else + *ptr++ = htonl((TCPOPT_NOP << 24) | + (TCPOPT_NOP << 16) | + (TCPOPT_TIMESTAMP << 8) | + TCPOLEN_TIMESTAMP); + + *ptr++ = htonl(opts->tsval); + *ptr++ = htonl(opts->tsecr); + } else if (options & XT_SYNPROXY_OPT_SACK_PERM) + *ptr++ = htonl((TCPOPT_NOP << 24) | + (TCPOPT_NOP << 16) | + (TCPOPT_SACK_PERM << 8) | + TCPOLEN_SACK_PERM); + + if (options & XT_SYNPROXY_OPT_WSCALE) + *ptr++ = htonl((TCPOPT_NOP << 24) | + (TCPOPT_WINDOW << 16) | + (TCPOLEN_WINDOW << 8) | + opts->wscale); +} +EXPORT_SYMBOL_GPL(synproxy_build_options); + +void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info, + struct synproxy_options *opts) +{ + opts->tsecr = opts->tsval; + opts->tsval = tcp_time_stamp & ~0x3f; + + if (opts->options & XT_SYNPROXY_OPT_WSCALE) + opts->tsval |= info->wscale; + else + opts->tsval |= 0xf; + + if (opts->options & XT_SYNPROXY_OPT_SACK_PERM) + opts->tsval |= 1 << 4; + + if (opts->options & XT_SYNPROXY_OPT_ECN) + opts->tsval |= 1 << 5; +} +EXPORT_SYMBOL_GPL(synproxy_init_timestamp_cookie); + +void synproxy_check_timestamp_cookie(struct synproxy_options *opts) +{ + opts->wscale = opts->tsecr & 0xf; + if (opts->wscale != 0xf) + opts->options |= XT_SYNPROXY_OPT_WSCALE; + + opts->options |= opts->tsecr & (1 << 4) ? XT_SYNPROXY_OPT_SACK_PERM : 0; + + opts->options |= opts->tsecr & (1 << 5) ? XT_SYNPROXY_OPT_ECN : 0; +} +EXPORT_SYMBOL_GPL(synproxy_check_timestamp_cookie); + +unsigned int synproxy_tstamp_adjust(struct sk_buff *skb, + unsigned int protoff, + struct tcphdr *th, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + const struct nf_conn_synproxy *synproxy) +{ + unsigned int optoff, optend; + u32 *ptr, old; + + if (synproxy->tsoff == 0) + return 1; + + optoff = protoff + sizeof(struct tcphdr); + optend = protoff + th->doff * 4; + + if (!skb_make_writable(skb, optend)) + return 0; + + while (optoff < optend) { + unsigned char *op = skb->data + optoff; + + switch (op[0]) { + case TCPOPT_EOL: + return 1; + case TCPOPT_NOP: + optoff++; + continue; + default: + if (optoff + 1 == optend || + optoff + op[1] > optend || + op[1] < 2) + return 0; + if (op[0] == TCPOPT_TIMESTAMP && + op[1] == TCPOLEN_TIMESTAMP) { + if (CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY) { + ptr = (u32 *)&op[2]; + old = *ptr; + *ptr = htonl(ntohl(*ptr) - + synproxy->tsoff); + } else { + ptr = (u32 *)&op[6]; + old = *ptr; + *ptr = htonl(ntohl(*ptr) + + synproxy->tsoff); + } + inet_proto_csum_replace4(&th->check, skb, + old, *ptr, 0); + return 1; + } + optoff += op[1]; + } + } + return 1; +} +EXPORT_SYMBOL_GPL(synproxy_tstamp_adjust); + +static struct nf_ct_ext_type nf_ct_synproxy_extend __read_mostly = { + .len = sizeof(struct nf_conn_synproxy), + .align = __alignof__(struct nf_conn_synproxy), + .id = NF_CT_EXT_SYNPROXY, +}; + +#ifdef CONFIG_PROC_FS +static void *synproxy_cpu_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq)); + int cpu; + + if (*pos == 0) + return SEQ_START_TOKEN; + + for (cpu = *pos - 1; cpu < nr_cpu_ids; cpu++) { + if (!cpu_possible(cpu)) + continue; + *pos = cpu + 1; + return per_cpu_ptr(snet->stats, cpu); + } + + return NULL; +} + +static void *synproxy_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq)); + int cpu; + + for (cpu = *pos; cpu < nr_cpu_ids; cpu++) { + if (!cpu_possible(cpu)) + continue; + *pos = cpu + 1; + return per_cpu_ptr(snet->stats, cpu); + } + + return NULL; +} + +static void synproxy_cpu_seq_stop(struct seq_file *seq, void *v) +{ + return; +} + +static int synproxy_cpu_seq_show(struct seq_file *seq, void *v) +{ + struct synproxy_stats *stats = v; + + if (v == SEQ_START_TOKEN) { + seq_printf(seq, "entries\t\tsyn_received\t" + "cookie_invalid\tcookie_valid\t" + "cookie_retrans\tconn_reopened\n"); + return 0; + } + + seq_printf(seq, "%08x\t%08x\t%08x\t%08x\t%08x\t%08x\n", 0, + stats->syn_received, + stats->cookie_invalid, + stats->cookie_valid, + stats->cookie_retrans, + stats->conn_reopened); + + return 0; +} + +static const struct seq_operations synproxy_cpu_seq_ops = { + .start = synproxy_cpu_seq_start, + .next = synproxy_cpu_seq_next, + .stop = synproxy_cpu_seq_stop, + .show = synproxy_cpu_seq_show, +}; + +static int synproxy_cpu_seq_open(struct inode *inode, struct file *file) +{ + return seq_open_net(inode, file, &synproxy_cpu_seq_ops, + sizeof(struct seq_net_private)); +} + +static const struct file_operations synproxy_cpu_seq_fops = { + .owner = THIS_MODULE, + .open = synproxy_cpu_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_net, +}; + +static int __net_init synproxy_proc_init(struct net *net) +{ + if (!proc_create("synproxy", S_IRUGO, net->proc_net_stat, + &synproxy_cpu_seq_fops)) + return -ENOMEM; + return 0; +} + +static void __net_exit synproxy_proc_exit(struct net *net) +{ + remove_proc_entry("synproxy", net->proc_net_stat); +} +#else +static int __net_init synproxy_proc_init(struct net *net) +{ + return 0; +} + +static void __net_exit synproxy_proc_exit(struct net *net) +{ + return; +} +#endif /* CONFIG_PROC_FS */ + +static int __net_init synproxy_net_init(struct net *net) +{ + struct synproxy_net *snet = synproxy_pernet(net); + struct nf_conntrack_tuple t; + struct nf_conn *ct; + int err = -ENOMEM; + + memset(&t, 0, sizeof(t)); + ct = nf_conntrack_alloc(net, 0, &t, &t, GFP_KERNEL); + if (IS_ERR(ct)) { + err = PTR_ERR(ct); + goto err1; + } + + __set_bit(IPS_TEMPLATE_BIT, &ct->status); + __set_bit(IPS_CONFIRMED_BIT, &ct->status); + if (!nfct_seqadj_ext_add(ct)) + goto err2; + if (!nfct_synproxy_ext_add(ct)) + goto err2; + + snet->tmpl = ct; + + snet->stats = alloc_percpu(struct synproxy_stats); + if (snet->stats == NULL) + goto err2; + + err = synproxy_proc_init(net); + if (err < 0) + goto err3; + + return 0; + +err3: + free_percpu(snet->stats); +err2: + nf_conntrack_free(ct); +err1: + return err; +} + +static void __net_exit synproxy_net_exit(struct net *net) +{ + struct synproxy_net *snet = synproxy_pernet(net); + + nf_conntrack_free(snet->tmpl); + synproxy_proc_exit(net); + free_percpu(snet->stats); +} + +static struct pernet_operations synproxy_net_ops = { + .init = synproxy_net_init, + .exit = synproxy_net_exit, + .id = &synproxy_net_id, + .size = sizeof(struct synproxy_net), +}; + +static int __init synproxy_core_init(void) +{ + int err; + + err = nf_ct_extend_register(&nf_ct_synproxy_extend); + if (err < 0) + goto err1; + + err = register_pernet_subsys(&synproxy_net_ops); + if (err < 0) + goto err2; + + return 0; + +err2: + nf_ct_extend_unregister(&nf_ct_synproxy_extend); +err1: + return err; +} + +static void __exit synproxy_core_exit(void) +{ + unregister_pernet_subsys(&synproxy_net_ops); + nf_ct_extend_unregister(&nf_ct_synproxy_extend); +} + +module_init(synproxy_core_init); +module_exit(synproxy_core_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); -- cgit v1.2.3-71-gd317 From 81eb6a1487718a89621a0e0be7fafd0cd7c429a4 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 27 Aug 2013 08:50:15 +0200 Subject: net: syncookies: export cookie_v6_init_sequence/cookie_v6_check Extract the local TCP stack independant parts of tcp_v6_init_sequence() and cookie_v6_check() and export them for use by the upcoming IPv6 SYNPROXY target. Signed-off-by: Patrick McHardy Acked-by: David S. Miller Tested-by: Martin Topholm Signed-off-by: Jesper Dangaard Brouer Signed-off-by: Pablo Neira Ayuso --- include/net/tcp.h | 4 ++++ net/ipv6/syncookies.c | 25 ++++++++++++++++--------- 2 files changed, 20 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 8e2b63678a83..dd5e16f66f84 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -499,8 +499,12 @@ extern bool cookie_check_timestamp(struct tcp_options_received *opt, struct net *net, bool *ecn_ok); /* From net/ipv6/syncookies.c */ +extern int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th, + u32 cookie); extern struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb); #ifdef CONFIG_SYN_COOKIES +extern u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph, + const struct tcphdr *th, u16 *mssp); extern __u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb, __u16 *mss); #else diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index d5dda20bd717..bf63ac8a49b9 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -112,32 +112,38 @@ static __u32 check_tcp_syn_cookie(__u32 cookie, const struct in6_addr *saddr, & COOKIEMASK; } -__u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb, __u16 *mssp) +u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph, + const struct tcphdr *th, __u16 *mssp) { - const struct ipv6hdr *iph = ipv6_hdr(skb); - const struct tcphdr *th = tcp_hdr(skb); int mssind; const __u16 mss = *mssp; - tcp_synq_overflow(sk); - for (mssind = ARRAY_SIZE(msstab) - 1; mssind ; mssind--) if (mss >= msstab[mssind]) break; *mssp = msstab[mssind]; - NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); - return secure_tcp_syn_cookie(&iph->saddr, &iph->daddr, th->source, th->dest, ntohl(th->seq), jiffies / (HZ * 60), mssind); } +EXPORT_SYMBOL_GPL(__cookie_v6_init_sequence); -static inline int cookie_check(const struct sk_buff *skb, __u32 cookie) +__u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb, __u16 *mssp) { const struct ipv6hdr *iph = ipv6_hdr(skb); const struct tcphdr *th = tcp_hdr(skb); + + tcp_synq_overflow(sk); + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESSENT); + + return __cookie_v6_init_sequence(iph, th, mssp); +} + +int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th, + __u32 cookie) +{ __u32 seq = ntohl(th->seq) - 1; __u32 mssind = check_tcp_syn_cookie(cookie, &iph->saddr, &iph->daddr, th->source, th->dest, seq, @@ -145,6 +151,7 @@ static inline int cookie_check(const struct sk_buff *skb, __u32 cookie) return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0; } +EXPORT_SYMBOL_GPL(__cookie_v6_check); struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) { @@ -167,7 +174,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) goto out; if (tcp_synq_no_recent_overflow(sk) || - (mss = cookie_check(skb, cookie)) == 0) { + (mss = __cookie_v6_check(ipv6_hdr(skb), th, cookie)) == 0) { NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED); goto out; } -- cgit v1.2.3-71-gd317 From 355fe568e285815e6f62fcd004ca80085ae46a69 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 27 Aug 2013 16:02:18 -0700 Subject: Revert "OMAP: UART: Keep the TX fifo full when possible" This reverts commit c4415084218c68c5ee2fc583431e89a78d896b19. Kevin writes: Hmm, another OMAP serial patch that wasn't Cc'd to linux-omap where OMAP users might have seen it. :( I just bisected a strange problem in linux-next on OMAP3 down to this patch. Reverting it fixes the problem. On OMAP3530 Beagle and Overo, after boot, doing a 'cat /proc/cpuinfo' was not returning to a prompt, suggesting something strange with the FIFO. Hitting return gets me back to a prompt. Greg, this one should also be dropped from tty-next until it can be further investgated and the problem solved. Reported-by: Kevin Hilman Cc: Dmitry Fink Cc: Alexander Savchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 3 +-- include/uapi/linux/serial_reg.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index b0f46e666e1a..816d1a23f9d0 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -350,8 +350,7 @@ static void transmit_chars(struct uart_omap_port *up, unsigned int lsr) serial_omap_stop_tx(&up->port); return; } - count = up->port.fifosize - - (serial_in(up, UART_OMAP_TXFIFO_LVL) & 0xFF); + count = up->port.fifosize / 4; do { serial_out(up, UART_TX, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index 97c26beae605..e6322605b138 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -366,7 +366,6 @@ #define UART_OMAP_MDR1_FIR_MODE 0x05 /* FIR mode */ #define UART_OMAP_MDR1_CIR_MODE 0x06 /* CIR mode */ #define UART_OMAP_MDR1_DISABLE 0x07 /* Disable (default state) */ -#define UART_OMAP_TXFIFO_LVL 0x1A /* TX FIFO fullness */ /* * These are definitions for the Exar XR17V35X and XR17(C|D)15X -- cgit v1.2.3-71-gd317 From f799878000c5a9a1e6a311dfd4faa50601dcb1f8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 27 Aug 2013 17:24:49 -0700 Subject: sysfs: add sysfs_create/remove_groups for when SYSFS is not enabled We need these functions for when CONFIG_SYSFS=n. Reported-by: Fengguang Wu Signed-off-by: Greg Kroah-Hartman --- include/linux/sysfs.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include') diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 69c1ff003628..e647a1aa0723 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -350,6 +350,11 @@ static inline int sysfs_create_group(struct kobject *kobj, return 0; } +static inline int sysfs_create_groups(struct kobject *kobj, + const struct attribute_group **groups) +{ +} + static inline int sysfs_update_group(struct kobject *kobj, const struct attribute_group *grp) { @@ -361,6 +366,11 @@ static inline void sysfs_remove_group(struct kobject *kobj, { } +static inline void sysfs_remove_groups(struct kobject *kobj, + const struct attribute_group **groups) +{ +} + static inline int sysfs_add_file_to_group(struct kobject *kobj, const struct attribute *attr, const char *group) { -- cgit v1.2.3-71-gd317 From aa514ce34b65e3dc01f95a0b470b39bbb7e09998 Mon Sep 17 00:00:00 2001 From: Gerhard Sittig Date: Mon, 22 Jul 2013 14:14:40 +0200 Subject: clk: wrap I/O access for improved portability the common clock drivers were motivated/initiated by ARM development and apparently assume little endian peripherals wrap register/peripherals access in the common code (div, gate, mux) in preparation of adding COMMON_CLK support for other platforms Signed-off-by: Gerhard Sittig Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 6 +++--- drivers/clk/clk-gate.c | 6 +++--- drivers/clk/clk-mux.c | 6 +++--- include/linux/clk-provider.h | 17 +++++++++++++++++ 4 files changed, 26 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 749372f87ec4..8d3009e44fba 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -104,7 +104,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, struct clk_divider *divider = to_clk_divider(hw); unsigned int div, val; - val = readl(divider->reg) >> divider->shift; + val = clk_readl(divider->reg) >> divider->shift; val &= div_mask(divider); div = _get_div(divider, val); @@ -230,11 +230,11 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { val = div_mask(divider) << (divider->shift + 16); } else { - val = readl(divider->reg); + val = clk_readl(divider->reg); val &= ~(div_mask(divider) << divider->shift); } val |= value << divider->shift; - writel(val, divider->reg); + clk_writel(val, divider->reg); if (divider->lock) spin_unlock_irqrestore(divider->lock, flags); diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 2b28a004c19e..4a58c55255bd 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -58,7 +58,7 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable) if (set) reg |= BIT(gate->bit_idx); } else { - reg = readl(gate->reg); + reg = clk_readl(gate->reg); if (set) reg |= BIT(gate->bit_idx); @@ -66,7 +66,7 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable) reg &= ~BIT(gate->bit_idx); } - writel(reg, gate->reg); + clk_writel(reg, gate->reg); if (gate->lock) spin_unlock_irqrestore(gate->lock, flags); @@ -89,7 +89,7 @@ static int clk_gate_is_enabled(struct clk_hw *hw) u32 reg; struct clk_gate *gate = to_clk_gate(hw); - reg = readl(gate->reg); + reg = clk_readl(gate->reg); /* if a set bit disables this clk, flip it before masking */ if (gate->flags & CLK_GATE_SET_TO_DISABLE) diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 0811633fcc4d..4f96ff3ba728 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -42,7 +42,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) * OTOH, pmd_trace_clk_mux_ck uses a separate bit for each clock, so * val = 0x4 really means "bit 2, index starts at bit 0" */ - val = readl(mux->reg) >> mux->shift; + val = clk_readl(mux->reg) >> mux->shift; val &= mux->mask; if (mux->table) { @@ -89,11 +89,11 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) if (mux->flags & CLK_MUX_HIWORD_MASK) { val = mux->mask << (mux->shift + 16); } else { - val = readl(mux->reg); + val = clk_readl(mux->reg); val &= ~(mux->mask << mux->shift); } val |= index << mux->shift; - writel(val, mux->reg); + clk_writel(val, mux->reg); if (mux->lock) spin_unlock_irqrestore(mux->lock, flags); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 1f0285b2f422..73bdb69f0c08 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -12,6 +12,7 @@ #define __LINUX_CLK_PROVIDER_H #include +#include #ifdef CONFIG_COMMON_CLK @@ -504,5 +505,21 @@ static inline const char *of_clk_get_parent_name(struct device_node *np, #define of_clk_init(matches) \ { while (0); } #endif /* CONFIG_OF */ + +/* + * wrap access to peripherals in accessor routines + * for improved portability across platforms + */ + +static inline u32 clk_readl(u32 __iomem *reg) +{ + return readl(reg); +} + +static inline void clk_writel(u32 val, u32 __iomem *reg) +{ + writel(val, reg); +} + #endif /* CONFIG_COMMON_CLK */ #endif /* CLK_PROVIDER_H */ -- cgit v1.2.3-71-gd317 From d7bf353fd0aa3e12060ce64c8a7b4aaf4336145c Mon Sep 17 00:00:00 2001 From: "Mark A. Greer" Date: Fri, 23 Aug 2013 19:21:03 -0700 Subject: bq24190_charger: Add support for TI BQ24190 Battery Charger Add driver support for the Texas Instruments BQ24190 battery charger. Some of the information provided by the device is about the charger and other information is about the battery so create two power_supply objects (one for each) and provide the appropriate information for each one. The device has many fields that go beyond what is reasonable to report or modify using the existing 'POWER_SUPPLY_PROP_*' properties so the driver exports the register fields via sysfs. They are prefixed by 'f_' (for 'field') to make it easier to distinguish between a register field and a "normal" sysfs file exported by the power_supply infrastructure. Signed-off-by: Mark A. Greer Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 6 + drivers/power/Makefile | 1 + drivers/power/bq24190_charger.c | 1546 +++++++++++++++++++++++++++++++++ include/linux/power/bq24190_charger.h | 16 + 4 files changed, 1569 insertions(+) create mode 100644 drivers/power/bq24190_charger.c create mode 100644 include/linux/power/bq24190_charger.h (limited to 'include') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index dcc0d9e5817d..e8970a65ba52 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -334,6 +334,12 @@ config CHARGER_BQ2415X You'll need this driver to charge batteries on e.g. Nokia RX-51/N900. +config CHARGER_BQ24190 + tristate "TI BQ24190 battery charger driver" + depends on I2C && GPIOLIB + help + Say Y to enable support for the TI BQ24190 battery charger. + config CHARGER_SMB347 tristate "Summit Microelectronics SMB347 Battery Charger" depends on I2C diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 653bf6ceff30..4ae45339e32c 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o +obj-$(CONFIG_CHARGER_BQ24190) += bq24190_charger.o obj-$(CONFIG_POWER_AVS) += avs/ obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c new file mode 100644 index 000000000000..2b0f0e0b58a2 --- /dev/null +++ b/drivers/power/bq24190_charger.c @@ -0,0 +1,1546 @@ +/* + * Driver for the TI bq24190 battery charger. + * + * Author: Mark A. Greer + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#define BQ24190_MANUFACTURER "Texas Instruments" + +#define BQ24190_REG_ISC 0x00 /* Input Source Control */ +#define BQ24190_REG_ISC_EN_HIZ_MASK BIT(7) +#define BQ24190_REG_ISC_EN_HIZ_SHIFT 7 +#define BQ24190_REG_ISC_VINDPM_MASK (BIT(6) | BIT(5) | BIT(4) | \ + BIT(3)) +#define BQ24190_REG_ISC_VINDPM_SHIFT 3 +#define BQ24190_REG_ISC_IINLIM_MASK (BIT(2) | BIT(1) | BIT(0)) +#define BQ24190_REG_ISC_IINLIM_SHIFT 0 + +#define BQ24190_REG_POC 0x01 /* Power-On Configuration */ +#define BQ24190_REG_POC_RESET_MASK BIT(7) +#define BQ24190_REG_POC_RESET_SHIFT 7 +#define BQ24190_REG_POC_WDT_RESET_MASK BIT(6) +#define BQ24190_REG_POC_WDT_RESET_SHIFT 6 +#define BQ24190_REG_POC_CHG_CONFIG_MASK (BIT(5) | BIT(4)) +#define BQ24190_REG_POC_CHG_CONFIG_SHIFT 4 +#define BQ24190_REG_POC_SYS_MIN_MASK (BIT(3) | BIT(2) | BIT(1)) +#define BQ24190_REG_POC_SYS_MIN_SHIFT 1 +#define BQ24190_REG_POC_BOOST_LIM_MASK BIT(0) +#define BQ24190_REG_POC_BOOST_LIM_SHIFT 0 + +#define BQ24190_REG_CCC 0x02 /* Charge Current Control */ +#define BQ24190_REG_CCC_ICHG_MASK (BIT(7) | BIT(6) | BIT(5) | \ + BIT(4) | BIT(3) | BIT(2)) +#define BQ24190_REG_CCC_ICHG_SHIFT 2 +#define BQ24190_REG_CCC_FORCE_20PCT_MASK BIT(0) +#define BQ24190_REG_CCC_FORCE_20PCT_SHIFT 0 + +#define BQ24190_REG_PCTCC 0x03 /* Pre-charge/Termination Current Cntl */ +#define BQ24190_REG_PCTCC_IPRECHG_MASK (BIT(7) | BIT(6) | BIT(5) | \ + BIT(4)) +#define BQ24190_REG_PCTCC_IPRECHG_SHIFT 4 +#define BQ24190_REG_PCTCC_ITERM_MASK (BIT(3) | BIT(2) | BIT(1) | \ + BIT(0)) +#define BQ24190_REG_PCTCC_ITERM_SHIFT 0 + +#define BQ24190_REG_CVC 0x04 /* Charge Voltage Control */ +#define BQ24190_REG_CVC_VREG_MASK (BIT(7) | BIT(6) | BIT(5) | \ + BIT(4) | BIT(3) | BIT(2)) +#define BQ24190_REG_CVC_VREG_SHIFT 2 +#define BQ24190_REG_CVC_BATLOWV_MASK BIT(1) +#define BQ24190_REG_CVC_BATLOWV_SHIFT 1 +#define BQ24190_REG_CVC_VRECHG_MASK BIT(0) +#define BQ24190_REG_CVC_VRECHG_SHIFT 0 + +#define BQ24190_REG_CTTC 0x05 /* Charge Term/Timer Control */ +#define BQ24190_REG_CTTC_EN_TERM_MASK BIT(7) +#define BQ24190_REG_CTTC_EN_TERM_SHIFT 7 +#define BQ24190_REG_CTTC_TERM_STAT_MASK BIT(6) +#define BQ24190_REG_CTTC_TERM_STAT_SHIFT 6 +#define BQ24190_REG_CTTC_WATCHDOG_MASK (BIT(5) | BIT(4)) +#define BQ24190_REG_CTTC_WATCHDOG_SHIFT 4 +#define BQ24190_REG_CTTC_EN_TIMER_MASK BIT(3) +#define BQ24190_REG_CTTC_EN_TIMER_SHIFT 3 +#define BQ24190_REG_CTTC_CHG_TIMER_MASK (BIT(2) | BIT(1)) +#define BQ24190_REG_CTTC_CHG_TIMER_SHIFT 1 +#define BQ24190_REG_CTTC_JEITA_ISET_MASK BIT(0) +#define BQ24190_REG_CTTC_JEITA_ISET_SHIFT 0 + +#define BQ24190_REG_ICTRC 0x06 /* IR Comp/Thermal Regulation Control */ +#define BQ24190_REG_ICTRC_BAT_COMP_MASK (BIT(7) | BIT(6) | BIT(5)) +#define BQ24190_REG_ICTRC_BAT_COMP_SHIFT 5 +#define BQ24190_REG_ICTRC_VCLAMP_MASK (BIT(4) | BIT(3) | BIT(2)) +#define BQ24190_REG_ICTRC_VCLAMP_SHIFT 2 +#define BQ24190_REG_ICTRC_TREG_MASK (BIT(1) | BIT(0)) +#define BQ24190_REG_ICTRC_TREG_SHIFT 0 + +#define BQ24190_REG_MOC 0x07 /* Misc. Operation Control */ +#define BQ24190_REG_MOC_DPDM_EN_MASK BIT(7) +#define BQ24190_REG_MOC_DPDM_EN_SHIFT 7 +#define BQ24190_REG_MOC_TMR2X_EN_MASK BIT(6) +#define BQ24190_REG_MOC_TMR2X_EN_SHIFT 6 +#define BQ24190_REG_MOC_BATFET_DISABLE_MASK BIT(5) +#define BQ24190_REG_MOC_BATFET_DISABLE_SHIFT 5 +#define BQ24190_REG_MOC_JEITA_VSET_MASK BIT(4) +#define BQ24190_REG_MOC_JEITA_VSET_SHIFT 4 +#define BQ24190_REG_MOC_INT_MASK_MASK (BIT(1) | BIT(0)) +#define BQ24190_REG_MOC_INT_MASK_SHIFT 0 + +#define BQ24190_REG_SS 0x08 /* System Status */ +#define BQ24190_REG_SS_VBUS_STAT_MASK (BIT(7) | BIT(6)) +#define BQ24190_REG_SS_VBUS_STAT_SHIFT 6 +#define BQ24190_REG_SS_CHRG_STAT_MASK (BIT(5) | BIT(4)) +#define BQ24190_REG_SS_CHRG_STAT_SHIFT 4 +#define BQ24190_REG_SS_DPM_STAT_MASK BIT(3) +#define BQ24190_REG_SS_DPM_STAT_SHIFT 3 +#define BQ24190_REG_SS_PG_STAT_MASK BIT(2) +#define BQ24190_REG_SS_PG_STAT_SHIFT 2 +#define BQ24190_REG_SS_THERM_STAT_MASK BIT(1) +#define BQ24190_REG_SS_THERM_STAT_SHIFT 1 +#define BQ24190_REG_SS_VSYS_STAT_MASK BIT(0) +#define BQ24190_REG_SS_VSYS_STAT_SHIFT 0 + +#define BQ24190_REG_F 0x09 /* Fault */ +#define BQ24190_REG_F_WATCHDOG_FAULT_MASK BIT(7) +#define BQ24190_REG_F_WATCHDOG_FAULT_SHIFT 7 +#define BQ24190_REG_F_BOOST_FAULT_MASK BIT(6) +#define BQ24190_REG_F_BOOST_FAULT_SHIFT 6 +#define BQ24190_REG_F_CHRG_FAULT_MASK (BIT(5) | BIT(4)) +#define BQ24190_REG_F_CHRG_FAULT_SHIFT 4 +#define BQ24190_REG_F_BAT_FAULT_MASK BIT(3) +#define BQ24190_REG_F_BAT_FAULT_SHIFT 3 +#define BQ24190_REG_F_NTC_FAULT_MASK (BIT(2) | BIT(1) | BIT(0)) +#define BQ24190_REG_F_NTC_FAULT_SHIFT 0 + +#define BQ24190_REG_VPRS 0x0A /* Vendor/Part/Revision Status */ +#define BQ24190_REG_VPRS_PN_MASK (BIT(5) | BIT(4) | BIT(3)) +#define BQ24190_REG_VPRS_PN_SHIFT 3 +#define BQ24190_REG_VPRS_PN_24190 0x4 +#define BQ24190_REG_VPRS_PN_24192 0x5 /* Also 24193 */ +#define BQ24190_REG_VPRS_PN_24192I 0x3 +#define BQ24190_REG_VPRS_TS_PROFILE_MASK BIT(2) +#define BQ24190_REG_VPRS_TS_PROFILE_SHIFT 2 +#define BQ24190_REG_VPRS_DEV_REG_MASK (BIT(1) | BIT(0)) +#define BQ24190_REG_VPRS_DEV_REG_SHIFT 0 + +/* + * The FAULT register is latched by the bq24190 (except for NTC_FAULT) + * so the first read after a fault returns the latched value and subsequent + * reads return the current value. In order to return the fault status + * to the user, have the interrupt handler save the reg's value and retrieve + * it in the appropriate health/status routine. Each routine has its own + * flag indicating whether it should use the value stored by the last run + * of the interrupt handler or do an actual reg read. That way each routine + * can report back whatever fault may have occured. + */ +struct bq24190_dev_info { + struct i2c_client *client; + struct device *dev; + struct power_supply charger; + struct power_supply battery; + char model_name[I2C_NAME_SIZE]; + kernel_ulong_t model; + unsigned int gpio_int; + unsigned int irq; + struct mutex f_reg_lock; + bool first_time; + bool charger_health_valid; + bool battery_health_valid; + bool battery_status_valid; + u8 f_reg; + u8 ss_reg; + u8 watchdog; +}; + +/* + * The tables below provide a 2-way mapping for the value that goes in + * the register field and the real-world value that it represents. + * The index of the array is the value that goes in the register; the + * number at that index in the array is the real-world value that it + * represents. + */ +/* REG02[7:2] (ICHG) in uAh */ +static const int bq24190_ccc_ichg_values[] = { + 512000, 576000, 640000, 704000, 768000, 832000, 896000, 960000, + 1024000, 1088000, 1152000, 1216000, 1280000, 1344000, 1408000, 1472000, + 1536000, 1600000, 1664000, 1728000, 1792000, 1856000, 1920000, 1984000, + 2048000, 2112000, 2176000, 2240000, 2304000, 2368000, 2432000, 2496000, + 2560000, 2624000, 2688000, 2752000, 2816000, 2880000, 2944000, 3008000, + 3072000, 3136000, 3200000, 3264000, 3328000, 3392000, 3456000, 3520000, + 3584000, 3648000, 3712000, 3776000, 3840000, 3904000, 3968000, 4032000, + 4096000, 4160000, 4224000, 4288000, 4352000, 4416000, 4480000, 4544000 +}; + +/* REG04[7:2] (VREG) in uV */ +static const int bq24190_cvc_vreg_values[] = { + 3504000, 3520000, 3536000, 3552000, 3568000, 3584000, 3600000, 3616000, + 3632000, 3648000, 3664000, 3680000, 3696000, 3712000, 3728000, 3744000, + 3760000, 3776000, 3792000, 3808000, 3824000, 3840000, 3856000, 3872000, + 3888000, 3904000, 3920000, 3936000, 3952000, 3968000, 3984000, 4000000, + 4016000, 4032000, 4048000, 4064000, 4080000, 4096000, 4112000, 4128000, + 4144000, 4160000, 4176000, 4192000, 4208000, 4224000, 4240000, 4256000, + 4272000, 4288000, 4304000, 4320000, 4336000, 4352000, 4368000, 4384000, + 4400000 +}; + +/* REG06[1:0] (TREG) in tenths of degrees Celcius */ +static const int bq24190_ictrc_treg_values[] = { + 600, 800, 1000, 1200 +}; + +/* + * Return the index in 'tbl' of greatest value that is less than or equal to + * 'val'. The index range returned is 0 to 'tbl_size' - 1. Assumes that + * the values in 'tbl' are sorted from smallest to largest and 'tbl_size' + * is less than 2^8. + */ +static u8 bq24190_find_idx(const int tbl[], int tbl_size, int v) +{ + int i; + + for (i = 1; i < tbl_size; i++) + if (v < tbl[i]) + break; + + return i - 1; +} + +/* Basic driver I/O routines */ + +static int bq24190_read(struct bq24190_dev_info *bdi, u8 reg, u8 *data) +{ + int ret; + + ret = i2c_smbus_read_byte_data(bdi->client, reg); + if (ret < 0) + return ret; + + *data = ret; + return 0; +} + +static int bq24190_write(struct bq24190_dev_info *bdi, u8 reg, u8 data) +{ + return i2c_smbus_write_byte_data(bdi->client, reg, data); +} + +static int bq24190_read_mask(struct bq24190_dev_info *bdi, u8 reg, + u8 mask, u8 shift, u8 *data) +{ + u8 v; + int ret; + + ret = bq24190_read(bdi, reg, &v); + if (ret < 0) + return ret; + + v &= mask; + v >>= shift; + *data = v; + + return 0; +} + +static int bq24190_write_mask(struct bq24190_dev_info *bdi, u8 reg, + u8 mask, u8 shift, u8 data) +{ + u8 v; + int ret; + + ret = bq24190_read(bdi, reg, &v); + if (ret < 0) + return ret; + + v &= ~mask; + v |= ((data << shift) & mask); + + return bq24190_write(bdi, reg, v); +} + +static int bq24190_get_field_val(struct bq24190_dev_info *bdi, + u8 reg, u8 mask, u8 shift, + const int tbl[], int tbl_size, + int *val) +{ + u8 v; + int ret; + + ret = bq24190_read_mask(bdi, reg, mask, shift, &v); + if (ret < 0) + return ret; + + v = (v >= tbl_size) ? (tbl_size - 1) : v; + *val = tbl[v]; + + return 0; +} + +static int bq24190_set_field_val(struct bq24190_dev_info *bdi, + u8 reg, u8 mask, u8 shift, + const int tbl[], int tbl_size, + int val) +{ + u8 idx; + + idx = bq24190_find_idx(tbl, tbl_size, val); + + return bq24190_write_mask(bdi, reg, mask, shift, idx); +} + +#ifdef CONFIG_SYSFS +/* + * There are a numerous options that are configurable on the bq24190 + * that go well beyond what the power_supply properties provide access to. + * Provide sysfs access to them so they can be examined and possibly modified + * on the fly. They will be provided for the charger power_supply object only + * and will be prefixed by 'f_' to make them easier to recognize. + */ + +#define BQ24190_SYSFS_FIELD(_name, r, f, m, store) \ +{ \ + .attr = __ATTR(f_##_name, m, bq24190_sysfs_show, store), \ + .reg = BQ24190_REG_##r, \ + .mask = BQ24190_REG_##r##_##f##_MASK, \ + .shift = BQ24190_REG_##r##_##f##_SHIFT, \ +} + +#define BQ24190_SYSFS_FIELD_RW(_name, r, f) \ + BQ24190_SYSFS_FIELD(_name, r, f, S_IWUSR | S_IRUGO, \ + bq24190_sysfs_store) + +#define BQ24190_SYSFS_FIELD_RO(_name, r, f) \ + BQ24190_SYSFS_FIELD(_name, r, f, S_IRUGO, NULL) + +static ssize_t bq24190_sysfs_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t bq24190_sysfs_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +struct bq24190_sysfs_field_info { + struct device_attribute attr; + u8 reg; + u8 mask; + u8 shift; +}; + +static struct bq24190_sysfs_field_info bq24190_sysfs_field_tbl[] = { + /* sysfs name reg field in reg */ + BQ24190_SYSFS_FIELD_RW(en_hiz, ISC, EN_HIZ), + BQ24190_SYSFS_FIELD_RW(vindpm, ISC, VINDPM), + BQ24190_SYSFS_FIELD_RW(iinlim, ISC, IINLIM), + BQ24190_SYSFS_FIELD_RW(chg_config, POC, CHG_CONFIG), + BQ24190_SYSFS_FIELD_RW(sys_min, POC, SYS_MIN), + BQ24190_SYSFS_FIELD_RW(boost_lim, POC, BOOST_LIM), + BQ24190_SYSFS_FIELD_RW(ichg, CCC, ICHG), + BQ24190_SYSFS_FIELD_RW(force_20_pct, CCC, FORCE_20PCT), + BQ24190_SYSFS_FIELD_RW(iprechg, PCTCC, IPRECHG), + BQ24190_SYSFS_FIELD_RW(iterm, PCTCC, ITERM), + BQ24190_SYSFS_FIELD_RW(vreg, CVC, VREG), + BQ24190_SYSFS_FIELD_RW(batlowv, CVC, BATLOWV), + BQ24190_SYSFS_FIELD_RW(vrechg, CVC, VRECHG), + BQ24190_SYSFS_FIELD_RW(en_term, CTTC, EN_TERM), + BQ24190_SYSFS_FIELD_RW(term_stat, CTTC, TERM_STAT), + BQ24190_SYSFS_FIELD_RO(watchdog, CTTC, WATCHDOG), + BQ24190_SYSFS_FIELD_RW(en_timer, CTTC, EN_TIMER), + BQ24190_SYSFS_FIELD_RW(chg_timer, CTTC, CHG_TIMER), + BQ24190_SYSFS_FIELD_RW(jeta_iset, CTTC, JEITA_ISET), + BQ24190_SYSFS_FIELD_RW(bat_comp, ICTRC, BAT_COMP), + BQ24190_SYSFS_FIELD_RW(vclamp, ICTRC, VCLAMP), + BQ24190_SYSFS_FIELD_RW(treg, ICTRC, TREG), + BQ24190_SYSFS_FIELD_RW(dpdm_en, MOC, DPDM_EN), + BQ24190_SYSFS_FIELD_RW(tmr2x_en, MOC, TMR2X_EN), + BQ24190_SYSFS_FIELD_RW(batfet_disable, MOC, BATFET_DISABLE), + BQ24190_SYSFS_FIELD_RW(jeita_vset, MOC, JEITA_VSET), + BQ24190_SYSFS_FIELD_RO(int_mask, MOC, INT_MASK), + BQ24190_SYSFS_FIELD_RO(vbus_stat, SS, VBUS_STAT), + BQ24190_SYSFS_FIELD_RO(chrg_stat, SS, CHRG_STAT), + BQ24190_SYSFS_FIELD_RO(dpm_stat, SS, DPM_STAT), + BQ24190_SYSFS_FIELD_RO(pg_stat, SS, PG_STAT), + BQ24190_SYSFS_FIELD_RO(therm_stat, SS, THERM_STAT), + BQ24190_SYSFS_FIELD_RO(vsys_stat, SS, VSYS_STAT), + BQ24190_SYSFS_FIELD_RO(watchdog_fault, F, WATCHDOG_FAULT), + BQ24190_SYSFS_FIELD_RO(boost_fault, F, BOOST_FAULT), + BQ24190_SYSFS_FIELD_RO(chrg_fault, F, CHRG_FAULT), + BQ24190_SYSFS_FIELD_RO(bat_fault, F, BAT_FAULT), + BQ24190_SYSFS_FIELD_RO(ntc_fault, F, NTC_FAULT), + BQ24190_SYSFS_FIELD_RO(pn, VPRS, PN), + BQ24190_SYSFS_FIELD_RO(ts_profile, VPRS, TS_PROFILE), + BQ24190_SYSFS_FIELD_RO(dev_reg, VPRS, DEV_REG), +}; + +static struct attribute * + bq24190_sysfs_attrs[ARRAY_SIZE(bq24190_sysfs_field_tbl) + 1]; + +static const struct attribute_group bq24190_sysfs_attr_group = { + .attrs = bq24190_sysfs_attrs, +}; + +static void bq24190_sysfs_init_attrs(void) +{ + int i, limit = ARRAY_SIZE(bq24190_sysfs_field_tbl); + + for (i = 0; i < limit; i++) + bq24190_sysfs_attrs[i] = &bq24190_sysfs_field_tbl[i].attr.attr; + + bq24190_sysfs_attrs[limit] = NULL; /* Has additional entry for this */ +} + +static struct bq24190_sysfs_field_info *bq24190_sysfs_field_lookup( + const char *name) +{ + int i, limit = ARRAY_SIZE(bq24190_sysfs_field_tbl); + + for (i = 0; i < limit; i++) + if (!strcmp(name, bq24190_sysfs_field_tbl[i].attr.attr.name)) + break; + + if (i >= limit) + return NULL; + + return &bq24190_sysfs_field_tbl[i]; +} + +static ssize_t bq24190_sysfs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq24190_dev_info *bdi = + container_of(psy, struct bq24190_dev_info, charger); + struct bq24190_sysfs_field_info *info; + int ret; + u8 v; + + info = bq24190_sysfs_field_lookup(attr->attr.name); + if (!info) + return -EINVAL; + + ret = bq24190_read_mask(bdi, info->reg, info->mask, info->shift, &v); + if (ret) + return ret; + + return scnprintf(buf, PAGE_SIZE, "%hhx\n", v); +} + +static ssize_t bq24190_sysfs_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct power_supply *psy = dev_get_drvdata(dev); + struct bq24190_dev_info *bdi = + container_of(psy, struct bq24190_dev_info, charger); + struct bq24190_sysfs_field_info *info; + int ret; + u8 v; + + info = bq24190_sysfs_field_lookup(attr->attr.name); + if (!info) + return -EINVAL; + + ret = kstrtou8(buf, 0, &v); + if (ret < 0) + return ret; + + ret = bq24190_write_mask(bdi, info->reg, info->mask, info->shift, v); + if (ret) + return ret; + + return count; +} + +static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) +{ + bq24190_sysfs_init_attrs(); + + return sysfs_create_group(&bdi->charger.dev->kobj, + &bq24190_sysfs_attr_group); +} + +static void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) +{ + sysfs_remove_group(&bdi->charger.dev->kobj, &bq24190_sysfs_attr_group); +} +#else +static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) +{ + return 0; +} + +static inline void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) {} +#endif + +/* + * According to the "Host Mode and default Mode" section of the + * manual, a write to any register causes the bq24190 to switch + * from default mode to host mode. It will switch back to default + * mode after a WDT timeout unless the WDT is turned off as well. + * So, by simply turning off the WDT, we accomplish both with the + * same write. + */ +static int bq24190_set_mode_host(struct bq24190_dev_info *bdi) +{ + int ret; + u8 v; + + ret = bq24190_read(bdi, BQ24190_REG_CTTC, &v); + if (ret < 0) + return ret; + + bdi->watchdog = ((v & BQ24190_REG_CTTC_WATCHDOG_MASK) >> + BQ24190_REG_CTTC_WATCHDOG_SHIFT); + v &= ~BQ24190_REG_CTTC_WATCHDOG_MASK; + + return bq24190_write(bdi, BQ24190_REG_CTTC, v); +} + +static int bq24190_register_reset(struct bq24190_dev_info *bdi) +{ + int ret, limit = 100; + u8 v; + + /* Reset the registers */ + ret = bq24190_write_mask(bdi, BQ24190_REG_POC, + BQ24190_REG_POC_RESET_MASK, + BQ24190_REG_POC_RESET_SHIFT, + 0x1); + if (ret < 0) + return ret; + + /* Reset bit will be cleared by hardware so poll until it is */ + do { + ret = bq24190_read_mask(bdi, BQ24190_REG_POC, + BQ24190_REG_POC_RESET_MASK, + BQ24190_REG_POC_RESET_SHIFT, + &v); + if (ret < 0) + return ret; + + if (!v) + break; + + udelay(10); + } while (--limit); + + if (!limit) + return -EIO; + + return 0; +} + +/* Charger power supply property routines */ + +static int bq24190_charger_get_charge_type(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + u8 v; + int type, ret; + + ret = bq24190_read_mask(bdi, BQ24190_REG_POC, + BQ24190_REG_POC_CHG_CONFIG_MASK, + BQ24190_REG_POC_CHG_CONFIG_SHIFT, + &v); + if (ret < 0) + return ret; + + /* If POC[CHG_CONFIG] (REG01[5:4]) == 0, charge is disabled */ + if (!v) { + type = POWER_SUPPLY_CHARGE_TYPE_NONE; + } else { + ret = bq24190_read_mask(bdi, BQ24190_REG_CCC, + BQ24190_REG_CCC_FORCE_20PCT_MASK, + BQ24190_REG_CCC_FORCE_20PCT_SHIFT, + &v); + if (ret < 0) + return ret; + + type = (v) ? POWER_SUPPLY_CHARGE_TYPE_TRICKLE : + POWER_SUPPLY_CHARGE_TYPE_FAST; + } + + val->intval = type; + + return 0; +} + +static int bq24190_charger_set_charge_type(struct bq24190_dev_info *bdi, + const union power_supply_propval *val) +{ + u8 chg_config, force_20pct, en_term; + int ret; + + /* + * According to the "Termination when REG02[0] = 1" section of + * the bq24190 manual, the trickle charge could be less than the + * termination current so it recommends turning off the termination + * function. + * + * Note: AFAICT from the datasheet, the user will have to manually + * turn off the charging when in 20% mode. If its not turned off, + * there could be battery damage. So, use this mode at your own risk. + */ + switch (val->intval) { + case POWER_SUPPLY_CHARGE_TYPE_NONE: + chg_config = 0x0; + break; + case POWER_SUPPLY_CHARGE_TYPE_TRICKLE: + chg_config = 0x1; + force_20pct = 0x1; + en_term = 0x0; + break; + case POWER_SUPPLY_CHARGE_TYPE_FAST: + chg_config = 0x1; + force_20pct = 0x0; + en_term = 0x1; + break; + default: + return -EINVAL; + } + + if (chg_config) { /* Enabling the charger */ + ret = bq24190_write_mask(bdi, BQ24190_REG_CCC, + BQ24190_REG_CCC_FORCE_20PCT_MASK, + BQ24190_REG_CCC_FORCE_20PCT_SHIFT, + force_20pct); + if (ret < 0) + return ret; + + ret = bq24190_write_mask(bdi, BQ24190_REG_CTTC, + BQ24190_REG_CTTC_EN_TERM_MASK, + BQ24190_REG_CTTC_EN_TERM_SHIFT, + en_term); + if (ret < 0) + return ret; + } + + return bq24190_write_mask(bdi, BQ24190_REG_POC, + BQ24190_REG_POC_CHG_CONFIG_MASK, + BQ24190_REG_POC_CHG_CONFIG_SHIFT, chg_config); +} + +static int bq24190_charger_get_health(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + u8 v; + int health, ret; + + mutex_lock(&bdi->f_reg_lock); + + if (bdi->charger_health_valid) { + v = bdi->f_reg; + bdi->charger_health_valid = false; + mutex_unlock(&bdi->f_reg_lock); + } else { + mutex_unlock(&bdi->f_reg_lock); + + ret = bq24190_read(bdi, BQ24190_REG_F, &v); + if (ret < 0) + return ret; + } + + if (v & BQ24190_REG_F_BOOST_FAULT_MASK) { + /* + * This could be over-current or over-voltage but there's + * no way to tell which. Return 'OVERVOLTAGE' since there + * isn't an 'OVERCURRENT' value defined that we can return + * even if it was over-current. + */ + health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + } else { + v &= BQ24190_REG_F_CHRG_FAULT_MASK; + v >>= BQ24190_REG_F_CHRG_FAULT_SHIFT; + + switch (v) { + case 0x0: /* Normal */ + health = POWER_SUPPLY_HEALTH_GOOD; + break; + case 0x1: /* Input Fault (VBUS OVP or VBATintval = health; + + return 0; +} + +static int bq24190_charger_get_online(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + u8 v; + int ret; + + ret = bq24190_read_mask(bdi, BQ24190_REG_SS, + BQ24190_REG_SS_PG_STAT_MASK, + BQ24190_REG_SS_PG_STAT_SHIFT, &v); + if (ret < 0) + return ret; + + val->intval = v; + return 0; +} + +static int bq24190_charger_get_current(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + u8 v; + int curr, ret; + + ret = bq24190_get_field_val(bdi, BQ24190_REG_CCC, + BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT, + bq24190_ccc_ichg_values, + ARRAY_SIZE(bq24190_ccc_ichg_values), &curr); + if (ret < 0) + return ret; + + ret = bq24190_read_mask(bdi, BQ24190_REG_CCC, + BQ24190_REG_CCC_FORCE_20PCT_MASK, + BQ24190_REG_CCC_FORCE_20PCT_SHIFT, &v); + if (ret < 0) + return ret; + + /* If FORCE_20PCT is enabled, then current is 20% of ICHG value */ + if (v) + curr /= 5; + + val->intval = curr; + return 0; +} + +static int bq24190_charger_get_current_max(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + int idx = ARRAY_SIZE(bq24190_ccc_ichg_values) - 1; + + val->intval = bq24190_ccc_ichg_values[idx]; + return 0; +} + +static int bq24190_charger_set_current(struct bq24190_dev_info *bdi, + const union power_supply_propval *val) +{ + u8 v; + int ret, curr = val->intval; + + ret = bq24190_read_mask(bdi, BQ24190_REG_CCC, + BQ24190_REG_CCC_FORCE_20PCT_MASK, + BQ24190_REG_CCC_FORCE_20PCT_SHIFT, &v); + if (ret < 0) + return ret; + + /* If FORCE_20PCT is enabled, have to multiply value passed in by 5 */ + if (v) + curr *= 5; + + return bq24190_set_field_val(bdi, BQ24190_REG_CCC, + BQ24190_REG_CCC_ICHG_MASK, BQ24190_REG_CCC_ICHG_SHIFT, + bq24190_ccc_ichg_values, + ARRAY_SIZE(bq24190_ccc_ichg_values), curr); +} + +static int bq24190_charger_get_voltage(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + int voltage, ret; + + ret = bq24190_get_field_val(bdi, BQ24190_REG_CVC, + BQ24190_REG_CVC_VREG_MASK, BQ24190_REG_CVC_VREG_SHIFT, + bq24190_cvc_vreg_values, + ARRAY_SIZE(bq24190_cvc_vreg_values), &voltage); + if (ret < 0) + return ret; + + val->intval = voltage; + return 0; +} + +static int bq24190_charger_get_voltage_max(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + int idx = ARRAY_SIZE(bq24190_cvc_vreg_values) - 1; + + val->intval = bq24190_cvc_vreg_values[idx]; + return 0; +} + +static int bq24190_charger_set_voltage(struct bq24190_dev_info *bdi, + const union power_supply_propval *val) +{ + return bq24190_set_field_val(bdi, BQ24190_REG_CVC, + BQ24190_REG_CVC_VREG_MASK, BQ24190_REG_CVC_VREG_SHIFT, + bq24190_cvc_vreg_values, + ARRAY_SIZE(bq24190_cvc_vreg_values), val->intval); +} + +static int bq24190_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, union power_supply_propval *val) +{ + struct bq24190_dev_info *bdi = + container_of(psy, struct bq24190_dev_info, charger); + int ret; + + dev_dbg(bdi->dev, "prop: %d\n", psp); + + pm_runtime_get_sync(bdi->dev); + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPE: + ret = bq24190_charger_get_charge_type(bdi, val); + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = bq24190_charger_get_health(bdi, val); + break; + case POWER_SUPPLY_PROP_ONLINE: + ret = bq24190_charger_get_online(bdi, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = bq24190_charger_get_current(bdi, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + ret = bq24190_charger_get_current_max(bdi, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = bq24190_charger_get_voltage(bdi, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + ret = bq24190_charger_get_voltage_max(bdi, val); + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_SYSTEM; + ret = 0; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = bdi->model_name; + ret = 0; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = BQ24190_MANUFACTURER; + ret = 0; + break; + default: + ret = -ENODATA; + } + + pm_runtime_put_sync(bdi->dev); + return ret; +} + +static int bq24190_charger_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct bq24190_dev_info *bdi = + container_of(psy, struct bq24190_dev_info, charger); + int ret; + + dev_dbg(bdi->dev, "prop: %d\n", psp); + + pm_runtime_get_sync(bdi->dev); + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPE: + ret = bq24190_charger_set_charge_type(bdi, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = bq24190_charger_set_current(bdi, val); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = bq24190_charger_set_voltage(bdi, val); + break; + default: + ret = -EINVAL; + } + + pm_runtime_put_sync(bdi->dev); + return ret; +} + +static int bq24190_charger_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = 1; + break; + default: + ret = 0; + } + + return ret; +} + +static enum power_supply_property bq24190_charger_properties[] = { + POWER_SUPPLY_PROP_TYPE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static char *bq24190_charger_supplied_to[] = { + "main-battery", +}; + +static void bq24190_charger_init(struct power_supply *charger) +{ + charger->name = "bq24190-charger"; + charger->type = POWER_SUPPLY_TYPE_USB; + charger->properties = bq24190_charger_properties; + charger->num_properties = ARRAY_SIZE(bq24190_charger_properties); + charger->supplied_to = bq24190_charger_supplied_to; + charger->num_supplies = ARRAY_SIZE(bq24190_charger_supplied_to); + charger->get_property = bq24190_charger_get_property; + charger->set_property = bq24190_charger_set_property; + charger->property_is_writeable = bq24190_charger_property_is_writeable; +} + +/* Battery power supply property routines */ + +static int bq24190_battery_get_status(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + u8 ss_reg, chrg_fault; + int status, ret; + + mutex_lock(&bdi->f_reg_lock); + + if (bdi->battery_status_valid) { + chrg_fault = bdi->f_reg; + bdi->battery_status_valid = false; + mutex_unlock(&bdi->f_reg_lock); + } else { + mutex_unlock(&bdi->f_reg_lock); + + ret = bq24190_read(bdi, BQ24190_REG_F, &chrg_fault); + if (ret < 0) + return ret; + } + + chrg_fault &= BQ24190_REG_F_CHRG_FAULT_MASK; + chrg_fault >>= BQ24190_REG_F_CHRG_FAULT_SHIFT; + + ret = bq24190_read(bdi, BQ24190_REG_SS, &ss_reg); + if (ret < 0) + return ret; + + /* + * The battery must be discharging when any of these are true: + * - there is no good power source; + * - there is a charge fault. + * Could also be discharging when in "supplement mode" but + * there is no way to tell when its in that mode. + */ + if (!(ss_reg & BQ24190_REG_SS_PG_STAT_MASK) || chrg_fault) { + status = POWER_SUPPLY_STATUS_DISCHARGING; + } else { + ss_reg &= BQ24190_REG_SS_CHRG_STAT_MASK; + ss_reg >>= BQ24190_REG_SS_CHRG_STAT_SHIFT; + + switch (ss_reg) { + case 0x0: /* Not Charging */ + status = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case 0x1: /* Pre-charge */ + case 0x2: /* Fast Charging */ + status = POWER_SUPPLY_STATUS_CHARGING; + break; + case 0x3: /* Charge Termination Done */ + status = POWER_SUPPLY_STATUS_FULL; + break; + default: + ret = -EIO; + } + } + + if (!ret) + val->intval = status; + + return ret; +} + +static int bq24190_battery_get_health(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + u8 v; + int health, ret; + + mutex_lock(&bdi->f_reg_lock); + + if (bdi->battery_health_valid) { + v = bdi->f_reg; + bdi->battery_health_valid = false; + mutex_unlock(&bdi->f_reg_lock); + } else { + mutex_unlock(&bdi->f_reg_lock); + + ret = bq24190_read(bdi, BQ24190_REG_F, &v); + if (ret < 0) + return ret; + } + + if (v & BQ24190_REG_F_BAT_FAULT_MASK) { + health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + } else { + v &= BQ24190_REG_F_NTC_FAULT_MASK; + v >>= BQ24190_REG_F_NTC_FAULT_SHIFT; + + switch (v) { + case 0x0: /* Normal */ + health = POWER_SUPPLY_HEALTH_GOOD; + break; + case 0x1: /* TS1 Cold */ + case 0x3: /* TS2 Cold */ + case 0x5: /* Both Cold */ + health = POWER_SUPPLY_HEALTH_COLD; + break; + case 0x2: /* TS1 Hot */ + case 0x4: /* TS2 Hot */ + case 0x6: /* Both Hot */ + health = POWER_SUPPLY_HEALTH_OVERHEAT; + break; + default: + health = POWER_SUPPLY_HEALTH_UNKNOWN; + } + } + + val->intval = health; + return 0; +} + +static int bq24190_battery_get_online(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + u8 batfet_disable; + int ret; + + ret = bq24190_read_mask(bdi, BQ24190_REG_MOC, + BQ24190_REG_MOC_BATFET_DISABLE_MASK, + BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, &batfet_disable); + if (ret < 0) + return ret; + + val->intval = !batfet_disable; + return 0; +} + +static int bq24190_battery_set_online(struct bq24190_dev_info *bdi, + const union power_supply_propval *val) +{ + return bq24190_write_mask(bdi, BQ24190_REG_MOC, + BQ24190_REG_MOC_BATFET_DISABLE_MASK, + BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, !val->intval); +} + +static int bq24190_battery_get_temp_alert_max(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + int temp, ret; + + ret = bq24190_get_field_val(bdi, BQ24190_REG_ICTRC, + BQ24190_REG_ICTRC_TREG_MASK, + BQ24190_REG_ICTRC_TREG_SHIFT, + bq24190_ictrc_treg_values, + ARRAY_SIZE(bq24190_ictrc_treg_values), &temp); + if (ret < 0) + return ret; + + val->intval = temp; + return 0; +} + +static int bq24190_battery_set_temp_alert_max(struct bq24190_dev_info *bdi, + const union power_supply_propval *val) +{ + return bq24190_set_field_val(bdi, BQ24190_REG_ICTRC, + BQ24190_REG_ICTRC_TREG_MASK, + BQ24190_REG_ICTRC_TREG_SHIFT, + bq24190_ictrc_treg_values, + ARRAY_SIZE(bq24190_ictrc_treg_values), val->intval); +} + +static int bq24190_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, union power_supply_propval *val) +{ + struct bq24190_dev_info *bdi = + container_of(psy, struct bq24190_dev_info, battery); + int ret; + + dev_dbg(bdi->dev, "prop: %d\n", psp); + + pm_runtime_get_sync(bdi->dev); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + ret = bq24190_battery_get_status(bdi, val); + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = bq24190_battery_get_health(bdi, val); + break; + case POWER_SUPPLY_PROP_ONLINE: + ret = bq24190_battery_get_online(bdi, val); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + /* Could be Li-on or Li-polymer but no way to tell which */ + val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + ret = 0; + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + ret = bq24190_battery_get_temp_alert_max(bdi, val); + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_SYSTEM; + ret = 0; + break; + default: + ret = -ENODATA; + } + + pm_runtime_put_sync(bdi->dev); + return ret; +} + +static int bq24190_battery_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct bq24190_dev_info *bdi = + container_of(psy, struct bq24190_dev_info, battery); + int ret; + + dev_dbg(bdi->dev, "prop: %d\n", psp); + + pm_runtime_put_sync(bdi->dev); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = bq24190_battery_set_online(bdi, val); + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + ret = bq24190_battery_set_temp_alert_max(bdi, val); + break; + default: + ret = -EINVAL; + } + + pm_runtime_put_sync(bdi->dev); + return ret; +} + +static int bq24190_battery_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + ret = 1; + break; + default: + ret = 0; + } + + return ret; +} + +static enum power_supply_property bq24190_battery_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_TEMP_ALERT_MAX, + POWER_SUPPLY_PROP_SCOPE, +}; + +static void bq24190_battery_init(struct power_supply *battery) +{ + battery->name = "bq24190-battery"; + battery->type = POWER_SUPPLY_TYPE_BATTERY; + battery->properties = bq24190_battery_properties; + battery->num_properties = ARRAY_SIZE(bq24190_battery_properties); + battery->get_property = bq24190_battery_get_property; + battery->set_property = bq24190_battery_set_property; + battery->property_is_writeable = bq24190_battery_property_is_writeable; +} + +static irqreturn_t bq24190_irq_handler_thread(int irq, void *data) +{ + struct bq24190_dev_info *bdi = data; + bool alert_userspace = false; + u8 ss_reg, f_reg; + int ret; + + pm_runtime_get_sync(bdi->dev); + + ret = bq24190_read(bdi, BQ24190_REG_SS, &ss_reg); + if (ret < 0) { + dev_err(bdi->dev, "Can't read SS reg: %d\n", ret); + goto out; + } + + if (ss_reg != bdi->ss_reg) { + /* + * The device is in host mode so when PG_STAT goes from 1->0 + * (i.e., power removed) HIZ needs to be disabled. + */ + if ((bdi->ss_reg & BQ24190_REG_SS_PG_STAT_MASK) && + !(ss_reg & BQ24190_REG_SS_PG_STAT_MASK)) { + ret = bq24190_write_mask(bdi, BQ24190_REG_ISC, + BQ24190_REG_ISC_EN_HIZ_MASK, + BQ24190_REG_ISC_EN_HIZ_SHIFT, + 0); + if (ret < 0) + dev_err(bdi->dev, "Can't access ISC reg: %d\n", + ret); + } + + bdi->ss_reg = ss_reg; + alert_userspace = true; + } + + mutex_lock(&bdi->f_reg_lock); + + ret = bq24190_read(bdi, BQ24190_REG_F, &f_reg); + if (ret < 0) { + mutex_unlock(&bdi->f_reg_lock); + dev_err(bdi->dev, "Can't read F reg: %d\n", ret); + goto out; + } + + if (f_reg != bdi->f_reg) { + bdi->f_reg = f_reg; + bdi->charger_health_valid = true; + bdi->battery_health_valid = true; + bdi->battery_status_valid = true; + + alert_userspace = true; + } + + mutex_unlock(&bdi->f_reg_lock); + + /* + * Sometimes bq24190 gives a steady trickle of interrupts even + * though the watchdog timer is turned off and neither the STATUS + * nor FAULT registers have changed. Weed out these sprurious + * interrupts so userspace isn't alerted for no reason. + * In addition, the chip always generates an interrupt after + * register reset so we should ignore that one (the very first + * interrupt received). + */ + if (alert_userspace && !bdi->first_time) { + power_supply_changed(&bdi->charger); + power_supply_changed(&bdi->battery); + bdi->first_time = false; + } + +out: + pm_runtime_put_sync(bdi->dev); + + dev_dbg(bdi->dev, "ss_reg: 0x%02x, f_reg: 0x%02x\n", ss_reg, f_reg); + + return IRQ_HANDLED; +} + +static int bq24190_hw_init(struct bq24190_dev_info *bdi) +{ + u8 v; + int ret; + + pm_runtime_get_sync(bdi->dev); + + /* First check that the device really is what its supposed to be */ + ret = bq24190_read_mask(bdi, BQ24190_REG_VPRS, + BQ24190_REG_VPRS_PN_MASK, + BQ24190_REG_VPRS_PN_SHIFT, + &v); + if (ret < 0) + goto out; + + if (v != bdi->model) { + ret = -ENODEV; + goto out; + } + + ret = bq24190_register_reset(bdi); + if (ret < 0) + goto out; + + ret = bq24190_set_mode_host(bdi); +out: + pm_runtime_put_sync(bdi->dev); + return ret; +} + +#ifdef CONFIG_OF +static int bq24190_setup_dt(struct bq24190_dev_info *bdi) +{ + bdi->irq = irq_of_parse_and_map(bdi->dev->of_node, 0); + if (bdi->irq <= 0) + return -1; + + return 0; +} +#else +static int bq24190_setup_dt(struct bq24190_dev_info *bdi) +{ + return -1; +} +#endif + +static int bq24190_setup_pdata(struct bq24190_dev_info *bdi, + struct bq24190_platform_data *pdata) +{ + int ret; + + if (!gpio_is_valid(pdata->gpio_int)) + return -1; + + ret = gpio_request(pdata->gpio_int, dev_name(bdi->dev)); + if (ret < 0) + return -1; + + ret = gpio_direction_input(pdata->gpio_int); + if (ret < 0) + goto out; + + bdi->irq = gpio_to_irq(pdata->gpio_int); + if (!bdi->irq) + goto out; + + bdi->gpio_int = pdata->gpio_int; + return 0; + +out: + gpio_free(pdata->gpio_int); + return -1; +} + +static int bq24190_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct device *dev = &client->dev; + struct bq24190_platform_data *pdata = client->dev.platform_data; + struct bq24190_dev_info *bdi; + int ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(dev, "No support for SMBUS_BYTE_DATA\n"); + return -ENODEV; + } + + bdi = devm_kzalloc(dev, sizeof(*bdi), GFP_KERNEL); + if (!bdi) { + dev_err(dev, "Can't alloc bdi struct\n"); + return -ENOMEM; + } + + bdi->client = client; + bdi->dev = dev; + bdi->model = id->driver_data; + strncpy(bdi->model_name, id->name, I2C_NAME_SIZE); + mutex_init(&bdi->f_reg_lock); + bdi->first_time = true; + bdi->charger_health_valid = false; + bdi->battery_health_valid = false; + bdi->battery_status_valid = false; + + i2c_set_clientdata(client, bdi); + + if (dev->of_node) + ret = bq24190_setup_dt(bdi); + else + ret = bq24190_setup_pdata(bdi, pdata); + + if (ret) { + dev_err(dev, "Can't get irq info\n"); + return -EINVAL; + } + + ret = devm_request_threaded_irq(dev, bdi->irq, NULL, + bq24190_irq_handler_thread, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "bq24190-charger", bdi); + if (ret < 0) { + dev_err(dev, "Can't set up irq handler\n"); + goto out1; + } + + pm_runtime_enable(dev); + pm_runtime_resume(dev); + + ret = bq24190_hw_init(bdi); + if (ret < 0) { + dev_err(dev, "Hardware init failed\n"); + goto out2; + } + + bq24190_charger_init(&bdi->charger); + + ret = power_supply_register(dev, &bdi->charger); + if (ret) { + dev_err(dev, "Can't register charger\n"); + goto out2; + } + + bq24190_battery_init(&bdi->battery); + + ret = power_supply_register(dev, &bdi->battery); + if (ret) { + dev_err(dev, "Can't register battery\n"); + goto out3; + } + + ret = bq24190_sysfs_create_group(bdi); + if (ret) { + dev_err(dev, "Can't create sysfs entries\n"); + goto out4; + } + + return 0; + +out4: + power_supply_unregister(&bdi->battery); +out3: + power_supply_unregister(&bdi->charger); +out2: + pm_runtime_disable(dev); +out1: + if (bdi->gpio_int) + gpio_free(bdi->gpio_int); + + return ret; +} + +static int bq24190_remove(struct i2c_client *client) +{ + struct bq24190_dev_info *bdi = i2c_get_clientdata(client); + + pm_runtime_get_sync(bdi->dev); + bq24190_register_reset(bdi); + pm_runtime_put_sync(bdi->dev); + + bq24190_sysfs_remove_group(bdi); + power_supply_unregister(&bdi->battery); + power_supply_unregister(&bdi->charger); + pm_runtime_disable(bdi->dev); + + if (bdi->gpio_int) + gpio_free(bdi->gpio_int); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int bq24190_pm_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq24190_dev_info *bdi = i2c_get_clientdata(client); + + pm_runtime_get_sync(bdi->dev); + bq24190_register_reset(bdi); + pm_runtime_put_sync(bdi->dev); + + return 0; +} + +static int bq24190_pm_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq24190_dev_info *bdi = i2c_get_clientdata(client); + + bdi->charger_health_valid = false; + bdi->battery_health_valid = false; + bdi->battery_status_valid = false; + + pm_runtime_get_sync(bdi->dev); + bq24190_register_reset(bdi); + pm_runtime_put_sync(bdi->dev); + + /* Things may have changed while suspended so alert upper layer */ + power_supply_changed(&bdi->charger); + power_supply_changed(&bdi->battery); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(bq24190_pm_ops, bq24190_pm_suspend, bq24190_pm_resume); + +/* + * Only support the bq24190 right now. The bq24192, bq24192i, and bq24193 + * are similar but not identical so the driver needs to be extended to + * support them. + */ +static const struct i2c_device_id bq24190_i2c_ids[] = { + { "bq24190", BQ24190_REG_VPRS_PN_24190 }, + { }, +}; + +#ifdef CONFIG_OF +static const struct of_device_id bq24190_of_match[] = { + { .compatible = "ti,bq24190", }, + { }, +}; +MODULE_DEVICE_TABLE(of, bq24190_of_match); +#else +static const struct of_device_id bq24190_of_match[] = { + { }, +}; +#endif + +static struct i2c_driver bq24190_driver = { + .probe = bq24190_probe, + .remove = bq24190_remove, + .id_table = bq24190_i2c_ids, + .driver = { + .name = "bq24190-charger", + .owner = THIS_MODULE, + .pm = &bq24190_pm_ops, + .of_match_table = of_match_ptr(bq24190_of_match), + }, +}; +module_i2c_driver(bq24190_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mark A. Greer "); +MODULE_ALIAS("i2c:bq24190-charger"); +MODULE_DESCRIPTION("TI BQ24190 Charger Driver"); diff --git a/include/linux/power/bq24190_charger.h b/include/linux/power/bq24190_charger.h new file mode 100644 index 000000000000..9f0283721cbc --- /dev/null +++ b/include/linux/power/bq24190_charger.h @@ -0,0 +1,16 @@ +/* + * Platform data for the TI bq24190 battery charger driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _BQ24190_CHARGER_H_ +#define _BQ24190_CHARGER_H_ + +struct bq24190_platform_data { + unsigned int gpio_int; /* GPIO pin that's connected to INT# */ +}; + +#endif -- cgit v1.2.3-71-gd317 From 948dcf96622814d2a850a12851d27824530a9747 Mon Sep 17 00:00:00 2001 From: Zoran Markovic Date: Fri, 2 Aug 2013 13:38:02 -0700 Subject: power_supply: Prevent suspend until power supply events are processed This patch, originally authored by Arve Hjonnevag and Todd Poynor, prevents the system from entering suspend mode until the power supply plug, unplug, or any other change of state event is fully processed. This guarantees that the screen lights up and displays the battery charging state. The implementation uses the power supply wakeup_source object. Cc: David Woodhouse Cc: Arve Hjonnevag Cc: Todd Poynor Cc: John Stultz Signed-off-by: Zoran Markovic Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_core.c | 38 ++++++++++++++++++++++++++++++++------ include/linux/power_supply.h | 3 +++ 2 files changed, 35 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 3b2d5df45e7a..00e667296360 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -67,23 +67,42 @@ static int __power_supply_changed_work(struct device *dev, void *data) static void power_supply_changed_work(struct work_struct *work) { + unsigned long flags; struct power_supply *psy = container_of(work, struct power_supply, changed_work); dev_dbg(psy->dev, "%s\n", __func__); - class_for_each_device(power_supply_class, NULL, psy, - __power_supply_changed_work); - - power_supply_update_leds(psy); - - kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); + spin_lock_irqsave(&psy->changed_lock, flags); + if (psy->changed) { + psy->changed = false; + spin_unlock_irqrestore(&psy->changed_lock, flags); + class_for_each_device(power_supply_class, NULL, psy, + __power_supply_changed_work); + power_supply_update_leds(psy); + kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); + spin_lock_irqsave(&psy->changed_lock, flags); + } + /* + * Dependent power supplies (e.g. battery) may have changed state + * as a result of this event, so poll again and hold the + * wakeup_source until all events are processed. + */ + if (!psy->changed) + pm_relax(psy->dev); + spin_unlock_irqrestore(&psy->changed_lock, flags); } void power_supply_changed(struct power_supply *psy) { + unsigned long flags; + dev_dbg(psy->dev, "%s\n", __func__); + spin_lock_irqsave(&psy->changed_lock, flags); + psy->changed = true; + pm_stay_awake(psy->dev); + spin_unlock_irqrestore(&psy->changed_lock, flags); schedule_work(&psy->changed_work); } EXPORT_SYMBOL_GPL(power_supply_changed); @@ -500,6 +519,11 @@ int power_supply_register(struct device *parent, struct power_supply *psy) goto check_supplies_failed; } + spin_lock_init(&psy->changed_lock); + rc = device_init_wakeup(dev, true); + if (rc) + goto wakeup_init_failed; + rc = kobject_set_name(&dev->kobj, "%s", psy->name); if (rc) goto kobject_set_name_failed; @@ -529,6 +553,7 @@ create_triggers_failed: register_cooler_failed: psy_unregister_thermal(psy); register_thermal_failed: +wakeup_init_failed: device_del(dev); kobject_set_name_failed: device_add_failed: @@ -546,6 +571,7 @@ void power_supply_unregister(struct power_supply *psy) power_supply_remove_triggers(psy); psy_unregister_cooler(psy); psy_unregister_thermal(psy); + device_init_wakeup(psy->dev, false); device_unregister(psy->dev); } EXPORT_SYMBOL_GPL(power_supply_unregister); diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 804b90643a85..5c2600630dc9 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -15,6 +15,7 @@ #include #include +#include struct device; @@ -194,6 +195,8 @@ struct power_supply { /* private */ struct device *dev; struct work_struct changed_work; + spinlock_t changed_lock; + bool changed; #ifdef CONFIG_THERMAL struct thermal_zone_device *tzd; struct thermal_cooling_device *tcd; -- cgit v1.2.3-71-gd317 From 3159269ef484954985cbbf1ff89976f090a18b24 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 26 Aug 2013 09:58:30 +0200 Subject: debugfs: provide debugfs_create_x64() when disabled commit 15b0beaa332b3923cc ("Add x64 support to debugfs") added debugfs_create_x64(), but forgot to provide it when debugfs is disabled, causing problems when code tries to use it even then. Provide the appropriate static inline. Cc: Huang Ying Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- include/linux/debugfs.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index d68b4ea7343c..263489d0788d 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -192,6 +192,13 @@ static inline struct dentry *debugfs_create_x32(const char *name, umode_t mode, return ERR_PTR(-ENODEV); } +static inline struct dentry *debugfs_create_x64(const char *name, umode_t mode, + struct dentry *parent, + u64 *value) +{ + return ERR_PTR(-ENODEV); +} + static inline struct dentry *debugfs_create_size_t(const char *name, umode_t mode, struct dentry *parent, size_t *value) -- cgit v1.2.3-71-gd317 From 03b054e9696c3cbd3d5905ec96da15acd0a2fe8d Mon Sep 17 00:00:00 2001 From: Sherman Yin Date: Tue, 27 Aug 2013 11:32:12 -0700 Subject: pinctrl: Pass all configs to driver on pin_config_set() When setting pin configuration in the pinctrl framework, pin_config_set() or pin_config_group_set() is called in a loop to set one configuration at a time for the specified pin or group. This patch 1) removes the loop and 2) changes the API to pass the whole pin config array to the driver. It is now up to the driver to loop through the configs. This allows the driver to potentially combine configs and reduce the number of writes to pin config registers. All c files changed have been build-tested to verify the change compiles and that the corresponding .o is successfully generated. Signed-off-by: Sherman Yin Reviewed-by: Christian Daudt Reviewed-by: Matt Porter Tested-by: Stephen Warren Acked-by: Laurent Pinchart Signed-off-by: Linus Walleij --- drivers/pinctrl/mvebu/pinctrl-mvebu.c | 26 +++-- drivers/pinctrl/pinconf.c | 42 ++++---- drivers/pinctrl/pinctrl-abx500.c | 187 ++++++++++++++++++---------------- drivers/pinctrl/pinctrl-at91.c | 48 +++++---- drivers/pinctrl/pinctrl-bcm2835.c | 43 ++++---- drivers/pinctrl/pinctrl-exynos5440.c | 113 ++++++++++---------- drivers/pinctrl/pinctrl-falcon.c | 63 +++++++----- drivers/pinctrl/pinctrl-imx.c | 28 ++--- drivers/pinctrl/pinctrl-mxs.c | 91 +++++++++-------- drivers/pinctrl/pinctrl-nomadik.c | 125 ++++++++++++----------- drivers/pinctrl/pinctrl-palmas.c | 134 +++++++++++++----------- drivers/pinctrl/pinctrl-rockchip.c | 57 +++++++---- drivers/pinctrl/pinctrl-samsung.c | 17 +++- drivers/pinctrl/pinctrl-single.c | 33 ++++-- drivers/pinctrl/pinctrl-st.c | 11 +- drivers/pinctrl/pinctrl-sunxi.c | 77 +++++++------- drivers/pinctrl/pinctrl-tegra.c | 69 +++++++------ drivers/pinctrl/pinctrl-tz1090-pdc.c | 135 +++++++++++++----------- drivers/pinctrl/pinctrl-tz1090.c | 140 ++++++++++++++----------- drivers/pinctrl/pinctrl-u300.c | 18 ++-- drivers/pinctrl/pinctrl-xway.c | 119 +++++++++++++--------- drivers/pinctrl/sh-pfc/pinctrl.c | 42 ++++---- drivers/pinctrl/vt8500/pinctrl-wmt.c | 54 +++++----- include/linux/pinctrl/pinconf.h | 6 +- 24 files changed, 952 insertions(+), 726 deletions(-) (limited to 'include') diff --git a/drivers/pinctrl/mvebu/pinctrl-mvebu.c b/drivers/pinctrl/mvebu/pinctrl-mvebu.c index bb7ddb1bc89f..e78c041c3c73 100644 --- a/drivers/pinctrl/mvebu/pinctrl-mvebu.c +++ b/drivers/pinctrl/mvebu/pinctrl-mvebu.c @@ -191,18 +191,27 @@ static int mvebu_pinconf_group_get(struct pinctrl_dev *pctldev, } static int mvebu_pinconf_group_set(struct pinctrl_dev *pctldev, - unsigned gid, unsigned long config) + unsigned gid, unsigned long *configs, + unsigned num_configs) { struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct mvebu_pinctrl_group *grp = &pctl->groups[gid]; + int i, ret; if (!grp->ctrl) return -EINVAL; - if (grp->ctrl->mpp_set) - return grp->ctrl->mpp_set(grp->ctrl, config); + for (i = 0; i < num_configs; i++) { + if (grp->ctrl->mpp_set) + ret = grp->ctrl->mpp_set(grp->ctrl, configs[i]); + else + ret = mvebu_common_mpp_set(pctl, grp, configs[i]); - return mvebu_common_mpp_set(pctl, grp, config); + if (ret) + return ret; + } /* for each config */ + + return 0; } static void mvebu_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, @@ -303,6 +312,7 @@ static int mvebu_pinmux_enable(struct pinctrl_dev *pctldev, unsigned fid, struct mvebu_pinctrl_group *grp = &pctl->groups[gid]; struct mvebu_mpp_ctrl_setting *setting; int ret; + unsigned long config; setting = mvebu_pinctrl_find_setting_by_name(pctl, grp, func->name); @@ -313,7 +323,8 @@ static int mvebu_pinmux_enable(struct pinctrl_dev *pctldev, unsigned fid, return -EINVAL; } - ret = mvebu_pinconf_group_set(pctldev, grp->gid, setting->val); + config = setting->val; + ret = mvebu_pinconf_group_set(pctldev, grp->gid, &config, 1); if (ret) { dev_err(pctl->dev, "cannot set group %s to %s\n", func->groups[gid], func->name); @@ -329,6 +340,7 @@ static int mvebu_pinmux_gpio_request_enable(struct pinctrl_dev *pctldev, struct mvebu_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct mvebu_pinctrl_group *grp; struct mvebu_mpp_ctrl_setting *setting; + unsigned long config; grp = mvebu_pinctrl_find_group_by_pid(pctl, offset); if (!grp) @@ -341,7 +353,9 @@ static int mvebu_pinmux_gpio_request_enable(struct pinctrl_dev *pctldev, if (!setting) return -ENOTSUPP; - return mvebu_pinconf_group_set(pctldev, grp->gid, setting->val); + config = setting->val; + + return mvebu_pinconf_group_set(pctldev, grp->gid, &config, 1); } static int mvebu_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev, diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c index 8da2d6db228c..a138965c01cb 100644 --- a/drivers/pinctrl/pinconf.c +++ b/drivers/pinctrl/pinconf.c @@ -158,7 +158,7 @@ int pinconf_apply_setting(struct pinctrl_setting const *setting) { struct pinctrl_dev *pctldev = setting->pctldev; const struct pinconf_ops *ops = pctldev->desc->confops; - int i, ret; + int ret; if (!ops) { dev_err(pctldev->dev, "missing confops\n"); @@ -171,17 +171,15 @@ int pinconf_apply_setting(struct pinctrl_setting const *setting) dev_err(pctldev->dev, "missing pin_config_set op\n"); return -EINVAL; } - for (i = 0; i < setting->data.configs.num_configs; i++) { - ret = ops->pin_config_set(pctldev, - setting->data.configs.group_or_pin, - setting->data.configs.configs[i]); - if (ret < 0) { - dev_err(pctldev->dev, - "pin_config_set op failed for pin %d config %08lx\n", - setting->data.configs.group_or_pin, - setting->data.configs.configs[i]); - return ret; - } + ret = ops->pin_config_set(pctldev, + setting->data.configs.group_or_pin, + setting->data.configs.configs, + setting->data.configs.num_configs); + if (ret < 0) { + dev_err(pctldev->dev, + "pin_config_set op failed for pin %d\n", + setting->data.configs.group_or_pin); + return ret; } break; case PIN_MAP_TYPE_CONFIGS_GROUP: @@ -190,17 +188,15 @@ int pinconf_apply_setting(struct pinctrl_setting const *setting) "missing pin_config_group_set op\n"); return -EINVAL; } - for (i = 0; i < setting->data.configs.num_configs; i++) { - ret = ops->pin_config_group_set(pctldev, - setting->data.configs.group_or_pin, - setting->data.configs.configs[i]); - if (ret < 0) { - dev_err(pctldev->dev, - "pin_config_group_set op failed for group %d config %08lx\n", - setting->data.configs.group_or_pin, - setting->data.configs.configs[i]); - return ret; - } + ret = ops->pin_config_group_set(pctldev, + setting->data.configs.group_or_pin, + setting->data.configs.configs, + setting->data.configs.num_configs); + if (ret < 0) { + dev_err(pctldev->dev, + "pin_config_group_set op failed for group %d\n", + setting->data.configs.group_or_pin); + return ret; } break; default: diff --git a/drivers/pinctrl/pinctrl-abx500.c b/drivers/pinctrl/pinctrl-abx500.c index 1d3f988c2c8b..8f25df06bc6b 100644 --- a/drivers/pinctrl/pinctrl-abx500.c +++ b/drivers/pinctrl/pinctrl-abx500.c @@ -1041,98 +1041,115 @@ static int abx500_pin_config_get(struct pinctrl_dev *pctldev, static int abx500_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, - unsigned long config) + unsigned long *configs, + unsigned num_configs) { struct abx500_pinctrl *pct = pinctrl_dev_get_drvdata(pctldev); struct gpio_chip *chip = &pct->chip; unsigned offset; int ret = -EINVAL; - 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_DISABLE: - ret = abx500_gpio_direction_input(chip, offset); - if (ret < 0) - goto out; - /* - * 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 (abx500_pullud_supported(chip, pin)) - ret = abx500_set_pull_updown(pct, - pin, - ABX500_GPIO_PULL_NONE); - else - /* Chip only supports pull down */ - ret = abx500_gpio_set_bits(chip, AB8500_GPIO_PUD1_REG, - offset, ABX500_GPIO_PULL_NONE); - break; - - case PIN_CONFIG_BIAS_PULL_DOWN: - ret = abx500_gpio_direction_input(chip, offset); - if (ret < 0) - goto out; - /* - * if argument = 1 set the pull down - * else clear the pull down - * 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 (abx500_pullud_supported(chip, pin)) - ret = abx500_set_pull_updown(pct, - pin, - 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 ? ABX500_GPIO_PULL_DOWN : ABX500_GPIO_PULL_NONE); - break; - - case PIN_CONFIG_BIAS_PULL_UP: - ret = abx500_gpio_direction_input(chip, offset); - if (ret < 0) - goto out; - /* - * if argument = 1 set the pull up - * else clear the pull up - */ - 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, do - * nothing - */ - if (abx500_pullud_supported(chip, pin)) - ret = abx500_set_pull_updown(pct, - pin, - argument ? ABX500_GPIO_PULL_UP : ABX500_GPIO_PULL_NONE); - break; + int i; + enum pin_config_param param; + enum pin_config_param argument; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + argument = pinconf_to_config_argument(configs[i]); + + dev_dbg(chip->dev, "pin %d [%#lx]: %s %s\n", + pin, configs[i], + (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_DISABLE: + ret = abx500_gpio_direction_input(chip, offset); + if (ret < 0) + goto out; + /* + * 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 (abx500_pullud_supported(chip, pin)) + ret = abx500_set_pull_updown(pct, + pin, + ABX500_GPIO_PULL_NONE); + else + /* Chip only supports pull down */ + ret = abx500_gpio_set_bits(chip, + AB8500_GPIO_PUD1_REG, offset, + ABX500_GPIO_PULL_NONE); + break; - case PIN_CONFIG_OUTPUT: - ret = abx500_gpio_direction_output(chip, offset, argument); - break; + case PIN_CONFIG_BIAS_PULL_DOWN: + ret = abx500_gpio_direction_input(chip, offset); + if (ret < 0) + goto out; + /* + * if argument = 1 set the pull down + * else clear the pull down + * 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 (abx500_pullud_supported(chip, pin)) + ret = abx500_set_pull_updown(pct, + pin, + 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 ? ABX500_GPIO_PULL_DOWN : + ABX500_GPIO_PULL_NONE); + break; - default: - dev_err(chip->dev, "illegal configuration requested\n"); - } + case PIN_CONFIG_BIAS_PULL_UP: + ret = abx500_gpio_direction_input(chip, offset); + if (ret < 0) + goto out; + /* + * if argument = 1 set the pull up + * else clear the pull up + */ + 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, do nothing + */ + if (abx500_pullud_supported(chip, pin)) + ret = abx500_set_pull_updown(pct, + pin, + argument ? ABX500_GPIO_PULL_UP : + ABX500_GPIO_PULL_NONE); + break; + + case PIN_CONFIG_OUTPUT: + ret = abx500_gpio_direction_output(chip, offset, + argument); + break; + + default: + dev_err(chip->dev, "illegal configuration requested\n"); + } + } /* for each config */ out: if (ret < 0) dev_err(pct->dev, "%s failed (%d)\n", __func__, ret); diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 7cce066c71b6..9685ff6c8b01 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -736,30 +736,40 @@ static int at91_pinconf_get(struct pinctrl_dev *pctldev, } static int at91_pinconf_set(struct pinctrl_dev *pctldev, - unsigned pin_id, unsigned long config) + unsigned pin_id, unsigned long *configs, + unsigned num_configs) { struct at91_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); unsigned mask; void __iomem *pio; - - dev_dbg(info->dev, "%s:%d, pin_id=%d, config=0x%lx", __func__, __LINE__, pin_id, config); - pio = pin_to_controller(info, pin_to_bank(pin_id)); - mask = pin_to_mask(pin_id % MAX_NB_GPIO_PER_BANK); - - if (config & PULL_UP && config & PULL_DOWN) - return -EINVAL; - - at91_mux_set_pullup(pio, mask, config & PULL_UP); - at91_mux_set_multidrive(pio, mask, config & MULTI_DRIVE); - if (info->ops->set_deglitch) - info->ops->set_deglitch(pio, mask, config & DEGLITCH); - if (info->ops->set_debounce) - info->ops->set_debounce(pio, mask, config & DEBOUNCE, + int i; + unsigned long config; + + for (i = 0; i < num_configs; i++) { + config = configs[i]; + + dev_dbg(info->dev, + "%s:%d, pin_id=%d, config=0x%lx", + __func__, __LINE__, pin_id, config); + pio = pin_to_controller(info, pin_to_bank(pin_id)); + mask = pin_to_mask(pin_id % MAX_NB_GPIO_PER_BANK); + + if (config & PULL_UP && config & PULL_DOWN) + return -EINVAL; + + at91_mux_set_pullup(pio, mask, config & PULL_UP); + at91_mux_set_multidrive(pio, mask, config & MULTI_DRIVE); + if (info->ops->set_deglitch) + info->ops->set_deglitch(pio, mask, config & DEGLITCH); + if (info->ops->set_debounce) + info->ops->set_debounce(pio, mask, config & DEBOUNCE, (config & DEBOUNCE_VAL) >> DEBOUNCE_VAL_SHIFT); - if (info->ops->set_pulldown) - info->ops->set_pulldown(pio, mask, config & PULL_DOWN); - if (info->ops->disable_schmitt_trig && config & DIS_SCHMIT) - info->ops->disable_schmitt_trig(pio, mask); + if (info->ops->set_pulldown) + info->ops->set_pulldown(pio, mask, config & PULL_DOWN); + if (info->ops->disable_schmitt_trig && config & DIS_SCHMIT) + info->ops->disable_schmitt_trig(pio, mask); + + } /* for each config */ return 0; } diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c index a1c88b30f71f..c05c1ef2cc3c 100644 --- a/drivers/pinctrl/pinctrl-bcm2835.c +++ b/drivers/pinctrl/pinctrl-bcm2835.c @@ -893,28 +893,35 @@ static int bcm2835_pinconf_get(struct pinctrl_dev *pctldev, } static int bcm2835_pinconf_set(struct pinctrl_dev *pctldev, - unsigned pin, unsigned long config) + unsigned pin, unsigned long *configs, + unsigned num_configs) { struct bcm2835_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev); - enum bcm2835_pinconf_param param = BCM2835_PINCONF_UNPACK_PARAM(config); - u16 arg = BCM2835_PINCONF_UNPACK_ARG(config); + enum bcm2835_pinconf_param param; + u16 arg; u32 off, bit; + int i; - if (param != BCM2835_PINCONF_PARAM_PULL) - return -EINVAL; - - off = GPIO_REG_OFFSET(pin); - bit = GPIO_REG_SHIFT(pin); - - bcm2835_gpio_wr(pc, GPPUD, arg & 3); - /* - * Docs say to wait 150 cycles, but not of what. We assume a - * 1 MHz clock here, which is pretty slow... - */ - udelay(150); - bcm2835_gpio_wr(pc, GPPUDCLK0 + (off * 4), BIT(bit)); - udelay(150); - bcm2835_gpio_wr(pc, GPPUDCLK0 + (off * 4), 0); + for (i = 0; i < num_configs; i++) { + param = BCM2835_PINCONF_UNPACK_PARAM(configs[i]); + arg = BCM2835_PINCONF_UNPACK_ARG(configs[i]); + + if (param != BCM2835_PINCONF_PARAM_PULL) + return -EINVAL; + + off = GPIO_REG_OFFSET(pin); + bit = GPIO_REG_SHIFT(pin); + + bcm2835_gpio_wr(pc, GPPUD, arg & 3); + /* + * Docs say to wait 150 cycles, but not of what. We assume a + * 1 MHz clock here, which is pretty slow... + */ + udelay(150); + bcm2835_gpio_wr(pc, GPPUDCLK0 + (off * 4), BIT(bit)); + udelay(150); + bcm2835_gpio_wr(pc, GPPUDCLK0 + (off * 4), 0); + } /* for each config */ return 0; } diff --git a/drivers/pinctrl/pinctrl-exynos5440.c b/drivers/pinctrl/pinctrl-exynos5440.c index 3b283fd898ff..544d469c5a7b 100644 --- a/drivers/pinctrl/pinctrl-exynos5440.c +++ b/drivers/pinctrl/pinctrl-exynos5440.c @@ -401,64 +401,71 @@ static const struct pinmux_ops exynos5440_pinmux_ops = { /* set the pin config settings for a specified pin */ static int exynos5440_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, - unsigned long config) + unsigned long *configs, + unsigned num_configs) { struct exynos5440_pinctrl_priv_data *priv; void __iomem *base; - enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE(config); - u32 cfg_value = PINCFG_UNPACK_VALUE(config); + enum pincfg_type cfg_type; + u32 cfg_value; u32 data; + int i; priv = pinctrl_dev_get_drvdata(pctldev); base = priv->reg_base; - switch (cfg_type) { - case PINCFG_TYPE_PUD: - /* first set pull enable/disable bit */ - data = readl(base + GPIO_PE); - data &= ~(1 << pin); - if (cfg_value) - data |= (1 << pin); - writel(data, base + GPIO_PE); - - /* then set pull up/down bit */ - data = readl(base + GPIO_PS); - data &= ~(1 << pin); - if (cfg_value == 2) - data |= (1 << pin); - writel(data, base + GPIO_PS); - break; - - case PINCFG_TYPE_DRV: - /* set the first bit of the drive strength */ - data = readl(base + GPIO_DS0); - data &= ~(1 << pin); - data |= ((cfg_value & 1) << pin); - writel(data, base + GPIO_DS0); - cfg_value >>= 1; - - /* set the second bit of the driver strength */ - data = readl(base + GPIO_DS1); - data &= ~(1 << pin); - data |= ((cfg_value & 1) << pin); - writel(data, base + GPIO_DS1); - break; - case PINCFG_TYPE_SKEW_RATE: - data = readl(base + GPIO_SR); - data &= ~(1 << pin); - data |= ((cfg_value & 1) << pin); - writel(data, base + GPIO_SR); - break; - case PINCFG_TYPE_INPUT_TYPE: - data = readl(base + GPIO_TYPE); - data &= ~(1 << pin); - data |= ((cfg_value & 1) << pin); - writel(data, base + GPIO_TYPE); - break; - default: - WARN_ON(1); - return -EINVAL; - } + for (i = 0; i < num_configs; i++) { + cfg_type = PINCFG_UNPACK_TYPE(configs[i]); + cfg_value = PINCFG_UNPACK_VALUE(configs[i]); + + switch (cfg_type) { + case PINCFG_TYPE_PUD: + /* first set pull enable/disable bit */ + data = readl(base + GPIO_PE); + data &= ~(1 << pin); + if (cfg_value) + data |= (1 << pin); + writel(data, base + GPIO_PE); + + /* then set pull up/down bit */ + data = readl(base + GPIO_PS); + data &= ~(1 << pin); + if (cfg_value == 2) + data |= (1 << pin); + writel(data, base + GPIO_PS); + break; + + case PINCFG_TYPE_DRV: + /* set the first bit of the drive strength */ + data = readl(base + GPIO_DS0); + data &= ~(1 << pin); + data |= ((cfg_value & 1) << pin); + writel(data, base + GPIO_DS0); + cfg_value >>= 1; + + /* set the second bit of the driver strength */ + data = readl(base + GPIO_DS1); + data &= ~(1 << pin); + data |= ((cfg_value & 1) << pin); + writel(data, base + GPIO_DS1); + break; + case PINCFG_TYPE_SKEW_RATE: + data = readl(base + GPIO_SR); + data &= ~(1 << pin); + data |= ((cfg_value & 1) << pin); + writel(data, base + GPIO_SR); + break; + case PINCFG_TYPE_INPUT_TYPE: + data = readl(base + GPIO_TYPE); + data &= ~(1 << pin); + data |= ((cfg_value & 1) << pin); + writel(data, base + GPIO_TYPE); + break; + default: + WARN_ON(1); + return -EINVAL; + } + } /* for each config */ return 0; } @@ -510,7 +517,8 @@ static int exynos5440_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, /* set the pin config settings for a specified pin group */ static int exynos5440_pinconf_group_set(struct pinctrl_dev *pctldev, - unsigned group, unsigned long config) + unsigned group, unsigned long *configs, + unsigned num_configs) { struct exynos5440_pinctrl_priv_data *priv; const unsigned int *pins; @@ -520,7 +528,8 @@ static int exynos5440_pinconf_group_set(struct pinctrl_dev *pctldev, pins = priv->pin_groups[group].pins; for (cnt = 0; cnt < priv->pin_groups[group].num_pins; cnt++) - exynos5440_pinconf_set(pctldev, pins[cnt], config); + exynos5440_pinconf_set(pctldev, pins[cnt], configs, + num_configs); return 0; } diff --git a/drivers/pinctrl/pinctrl-falcon.c b/drivers/pinctrl/pinctrl-falcon.c index 4e731f24df85..2e62689b5e9f 100644 --- a/drivers/pinctrl/pinctrl-falcon.c +++ b/drivers/pinctrl/pinctrl-falcon.c @@ -238,7 +238,8 @@ static int falcon_pinconf_group_get(struct pinctrl_dev *pctrldev, } static int falcon_pinconf_group_set(struct pinctrl_dev *pctrldev, - unsigned group, unsigned long config) + unsigned group, unsigned long *configs, + unsigned num_configs) { return -ENOTSUPP; } @@ -279,39 +280,47 @@ static int falcon_pinconf_get(struct pinctrl_dev *pctrldev, } static int falcon_pinconf_set(struct pinctrl_dev *pctrldev, - unsigned pin, unsigned long config) + unsigned pin, unsigned long *configs, + unsigned num_configs) { - enum ltq_pinconf_param param = LTQ_PINCONF_UNPACK_PARAM(config); - int arg = LTQ_PINCONF_UNPACK_ARG(config); + enum ltq_pinconf_param param; + int arg; struct ltq_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev); void __iomem *mem = info->membase[PORT(pin)]; u32 reg; + int i; - switch (param) { - case LTQ_PINCONF_PARAM_DRIVE_CURRENT: - reg = LTQ_PADC_DCC; - break; - - case LTQ_PINCONF_PARAM_SLEW_RATE: - reg = LTQ_PADC_SRC; - break; - - case LTQ_PINCONF_PARAM_PULL: - if (arg == 1) - reg = LTQ_PADC_PDEN; - else - reg = LTQ_PADC_PUEN; - break; + for (i = 0; i < num_configs; i++) { + param = LTQ_PINCONF_UNPACK_PARAM(configs[i]); + arg = LTQ_PINCONF_UNPACK_ARG(configs[i]); + + switch (param) { + case LTQ_PINCONF_PARAM_DRIVE_CURRENT: + reg = LTQ_PADC_DCC; + break; + + case LTQ_PINCONF_PARAM_SLEW_RATE: + reg = LTQ_PADC_SRC; + break; + + case LTQ_PINCONF_PARAM_PULL: + if (arg == 1) + reg = LTQ_PADC_PDEN; + else + reg = LTQ_PADC_PUEN; + break; + + default: + pr_err("%s: Invalid config param %04x\n", + pinctrl_dev_get_name(pctrldev), param); + return -ENOTSUPP; + } - default: - pr_err("%s: Invalid config param %04x\n", - pinctrl_dev_get_name(pctrldev), param); - return -ENOTSUPP; - } + pad_w32(mem, BIT(PORT_PIN(pin)), reg); + if (!(pad_r32(mem, reg) & BIT(PORT_PIN(pin)))) + return -ENOTSUPP; + } /* for each config */ - pad_w32(mem, BIT(PORT_PIN(pin)), reg); - if (!(pad_r32(mem, reg) & BIT(PORT_PIN(pin)))) - return -ENOTSUPP; return 0; } diff --git a/drivers/pinctrl/pinctrl-imx.c b/drivers/pinctrl/pinctrl-imx.c index 360249301315..d78dd813bff9 100644 --- a/drivers/pinctrl/pinctrl-imx.c +++ b/drivers/pinctrl/pinctrl-imx.c @@ -323,11 +323,13 @@ static int imx_pinconf_get(struct pinctrl_dev *pctldev, } static int imx_pinconf_set(struct pinctrl_dev *pctldev, - unsigned pin_id, unsigned long config) + unsigned pin_id, unsigned long *configs, + unsigned num_configs) { struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); const struct imx_pinctrl_soc_info *info = ipctl->info; const struct imx_pin_reg *pin_reg = &info->pin_regs[pin_id]; + int i; if (!(info->flags & ZERO_OFFSET_VALID) && !pin_reg->conf_reg) { dev_err(info->dev, "Pin(%s) does not support config function\n", @@ -338,17 +340,19 @@ static int imx_pinconf_set(struct pinctrl_dev *pctldev, dev_dbg(ipctl->dev, "pinconf set pin %s\n", info->pins[pin_id].name); - if (info->flags & SHARE_MUX_CONF_REG) { - u32 reg; - reg = readl(ipctl->base + pin_reg->conf_reg); - reg &= ~0xffff; - reg |= config; - writel(reg, ipctl->base + pin_reg->conf_reg); - } else { - writel(config, ipctl->base + pin_reg->conf_reg); - } - dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%lx\n", - pin_reg->conf_reg, config); + for (i = 0; i < num_configs; i++) { + if (info->flags & SHARE_MUX_CONF_REG) { + u32 reg; + reg = readl(ipctl->base + pin_reg->conf_reg); + reg &= ~0xffff; + reg |= configs[i]; + writel(reg, ipctl->base + pin_reg->conf_reg); + } else { + writel(configs[i], ipctl->base + pin_reg->conf_reg); + } + dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%lx\n", + pin_reg->conf_reg, configs[i]); + } /* for each config */ return 0; } diff --git a/drivers/pinctrl/pinctrl-mxs.c b/drivers/pinctrl/pinctrl-mxs.c index f5d56436ba7f..40c76f26998c 100644 --- a/drivers/pinctrl/pinctrl-mxs.c +++ b/drivers/pinctrl/pinctrl-mxs.c @@ -233,7 +233,8 @@ static int mxs_pinconf_get(struct pinctrl_dev *pctldev, } static int mxs_pinconf_set(struct pinctrl_dev *pctldev, - unsigned pin, unsigned long config) + unsigned pin, unsigned long *configs, + unsigned num_configs) { return -ENOTSUPP; } @@ -249,7 +250,8 @@ static int mxs_pinconf_group_get(struct pinctrl_dev *pctldev, } static int mxs_pinconf_group_set(struct pinctrl_dev *pctldev, - unsigned group, unsigned long config) + unsigned group, unsigned long *configs, + unsigned num_configs) { struct mxs_pinctrl_data *d = pinctrl_dev_get_drvdata(pctldev); struct mxs_group *g = &d->soc->groups[group]; @@ -257,49 +259,56 @@ static int mxs_pinconf_group_set(struct pinctrl_dev *pctldev, u8 ma, vol, pull, bank, shift; u16 pin; u32 i; + int n; + unsigned long config; - ma = CONFIG_TO_MA(config); - vol = CONFIG_TO_VOL(config); - pull = CONFIG_TO_PULL(config); - - for (i = 0; i < g->npins; i++) { - bank = PINID_TO_BANK(g->pins[i]); - pin = PINID_TO_PIN(g->pins[i]); - - /* drive */ - reg = d->base + d->soc->regs->drive; - reg += bank * 0x40 + pin / 8 * 0x10; - - /* mA */ - if (config & MA_PRESENT) { - shift = pin % 8 * 4; - writel(0x3 << shift, reg + CLR); - writel(ma << shift, reg + SET); - } - - /* vol */ - if (config & VOL_PRESENT) { - shift = pin % 8 * 4 + 2; - if (vol) - writel(1 << shift, reg + SET); - else - writel(1 << shift, reg + CLR); + for (n = 0; n < num_configs; n++) { + config = configs[n]; + + ma = CONFIG_TO_MA(config); + vol = CONFIG_TO_VOL(config); + pull = CONFIG_TO_PULL(config); + + for (i = 0; i < g->npins; i++) { + bank = PINID_TO_BANK(g->pins[i]); + pin = PINID_TO_PIN(g->pins[i]); + + /* drive */ + reg = d->base + d->soc->regs->drive; + reg += bank * 0x40 + pin / 8 * 0x10; + + /* mA */ + if (config & MA_PRESENT) { + shift = pin % 8 * 4; + writel(0x3 << shift, reg + CLR); + writel(ma << shift, reg + SET); + } + + /* vol */ + if (config & VOL_PRESENT) { + shift = pin % 8 * 4 + 2; + if (vol) + writel(1 << shift, reg + SET); + else + writel(1 << shift, reg + CLR); + } + + /* pull */ + if (config & PULL_PRESENT) { + reg = d->base + d->soc->regs->pull; + reg += bank * 0x10; + shift = pin; + if (pull) + writel(1 << shift, reg + SET); + else + writel(1 << shift, reg + CLR); + } } - /* pull */ - if (config & PULL_PRESENT) { - reg = d->base + d->soc->regs->pull; - reg += bank * 0x10; - shift = pin; - if (pull) - writel(1 << shift, reg + SET); - else - writel(1 << shift, reg + CLR); - } - } + /* cache the config value for mxs_pinconf_group_get() */ + g->config = config; - /* cache the config value for mxs_pinconf_group_get() */ - g->config = config; + } /* for each config */ return 0; } diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index f7c7279d081a..d7c3ae300fa7 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -1695,7 +1695,7 @@ static int nmk_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, } static int nmk_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, - unsigned long config) + unsigned long *configs, unsigned num_configs) { static const char *pullnames[] = { [NMK_GPIO_PULL_NONE] = "none", @@ -1712,20 +1712,9 @@ static int nmk_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, struct pinctrl_gpio_range *range; struct gpio_chip *chip; unsigned bit; - - /* - * The pin config contains pin number and altfunction fields, here - * we just ignore that part. It's being handled by the framework and - * pinmux callback respectively. - */ - pin_cfg_t cfg = (pin_cfg_t) config; - int pull = PIN_PULL(cfg); - int slpm = PIN_SLPM(cfg); - int output = PIN_DIR(cfg); - int val = PIN_VAL(cfg); - bool lowemi = PIN_LOWEMI(cfg); - bool gpiomode = PIN_GPIOMODE(cfg); - bool sleep = PIN_SLEEPMODE(cfg); + pin_cfg_t cfg; + int pull, slpm, output, val, i; + bool lowemi, gpiomode, sleep; range = nmk_match_gpio_range(pctldev, pin); if (!range) { @@ -1740,54 +1729,74 @@ static int nmk_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, chip = range->gc; nmk_chip = container_of(chip, struct nmk_gpio_chip, chip); - if (sleep) { - int slpm_pull = PIN_SLPM_PULL(cfg); - int slpm_output = PIN_SLPM_DIR(cfg); - int slpm_val = PIN_SLPM_VAL(cfg); - - /* All pins go into GPIO mode at sleep */ - gpiomode = true; - + for (i = 0; i < num_configs; i++) { /* - * The SLPM_* values are normal values + 1 to allow zero to - * mean "same as normal". + * The pin config contains pin number and altfunction fields, + * here we just ignore that part. It's being handled by the + * framework and pinmux callback respectively. */ - if (slpm_pull) - pull = slpm_pull - 1; - if (slpm_output) - output = slpm_output - 1; - if (slpm_val) - val = slpm_val - 1; - - dev_dbg(nmk_chip->chip.dev, "pin %d: sleep pull %s, dir %s, val %s\n", - pin, - slpm_pull ? pullnames[pull] : "same", - slpm_output ? (output ? "output" : "input") : "same", - slpm_val ? (val ? "high" : "low") : "same"); - } + cfg = (pin_cfg_t) configs[i]; + pull = PIN_PULL(cfg); + slpm = PIN_SLPM(cfg); + output = PIN_DIR(cfg); + val = PIN_VAL(cfg); + lowemi = PIN_LOWEMI(cfg); + gpiomode = PIN_GPIOMODE(cfg); + sleep = PIN_SLEEPMODE(cfg); + + if (sleep) { + int slpm_pull = PIN_SLPM_PULL(cfg); + int slpm_output = PIN_SLPM_DIR(cfg); + int slpm_val = PIN_SLPM_VAL(cfg); + + /* All pins go into GPIO mode at sleep */ + gpiomode = true; + + /* + * The SLPM_* values are normal values + 1 to allow zero + * to mean "same as normal". + */ + if (slpm_pull) + pull = slpm_pull - 1; + if (slpm_output) + output = slpm_output - 1; + if (slpm_val) + val = slpm_val - 1; + + dev_dbg(nmk_chip->chip.dev, + "pin %d: sleep pull %s, dir %s, val %s\n", + pin, + slpm_pull ? pullnames[pull] : "same", + slpm_output ? (output ? "output" : "input") + : "same", + slpm_val ? (val ? "high" : "low") : "same"); + } - dev_dbg(nmk_chip->chip.dev, "pin %d [%#lx]: pull %s, slpm %s (%s%s), lowemi %s\n", - pin, cfg, pullnames[pull], slpmnames[slpm], - output ? "output " : "input", - output ? (val ? "high" : "low") : "", - lowemi ? "on" : "off"); + dev_dbg(nmk_chip->chip.dev, + "pin %d [%#lx]: pull %s, slpm %s (%s%s), lowemi %s\n", + pin, cfg, pullnames[pull], slpmnames[slpm], + output ? "output " : "input", + output ? (val ? "high" : "low") : "", + lowemi ? "on" : "off"); - clk_enable(nmk_chip->clk); - bit = pin % NMK_GPIO_PER_CHIP; - if (gpiomode) - /* No glitch when going to GPIO mode */ - __nmk_gpio_set_mode(nmk_chip, bit, NMK_GPIO_ALT_GPIO); - if (output) - __nmk_gpio_make_output(nmk_chip, bit, val); - else { - __nmk_gpio_make_input(nmk_chip, bit); - __nmk_gpio_set_pull(nmk_chip, bit, pull); - } - /* TODO: isn't this only applicable on output pins? */ - __nmk_gpio_set_lowemi(nmk_chip, bit, lowemi); + clk_enable(nmk_chip->clk); + bit = pin % NMK_GPIO_PER_CHIP; + if (gpiomode) + /* No glitch when going to GPIO mode */ + __nmk_gpio_set_mode(nmk_chip, bit, NMK_GPIO_ALT_GPIO); + if (output) + __nmk_gpio_make_output(nmk_chip, bit, val); + else { + __nmk_gpio_make_input(nmk_chip, bit); + __nmk_gpio_set_pull(nmk_chip, bit, pull); + } + /* TODO: isn't this only applicable on output pins? */ + __nmk_gpio_set_lowemi(nmk_chip, bit, lowemi); + + __nmk_gpio_set_slpm(nmk_chip, bit, slpm); + clk_disable(nmk_chip->clk); + } /* for each config */ - __nmk_gpio_set_slpm(nmk_chip, bit, slpm); - clk_disable(nmk_chip->clk); return 0; } diff --git a/drivers/pinctrl/pinctrl-palmas.c b/drivers/pinctrl/pinctrl-palmas.c index 9550c33b2544..82638fac3cfa 100644 --- a/drivers/pinctrl/pinctrl-palmas.c +++ b/drivers/pinctrl/pinctrl-palmas.c @@ -854,17 +854,19 @@ static int palmas_pinconf_get(struct pinctrl_dev *pctldev, } static int palmas_pinconf_set(struct pinctrl_dev *pctldev, - unsigned pin, unsigned long config) + unsigned pin, unsigned long *configs, + unsigned num_configs) { struct palmas_pctrl_chip_info *pci = pinctrl_dev_get_drvdata(pctldev); - enum pin_config_param param = pinconf_to_config_param(config); - u16 param_val = pinconf_to_config_argument(config); + enum pin_config_param param; + u16 param_val; const struct palmas_pingroup *g; const struct palmas_pin_info *opt; int ret; int base, add, mask; int rval; int group_nr; + int i; for (group_nr = 0; group_nr < pci->num_pin_groups; ++group_nr) { if (pci->pin_groups[group_nr].pins[0] == pin) @@ -885,70 +887,77 @@ static int palmas_pinconf_set(struct pinctrl_dev *pctldev, return -ENOTSUPP; } - switch (param) { - case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: - return 0; - case PIN_CONFIG_BIAS_DISABLE: - case PIN_CONFIG_BIAS_PULL_UP: - case PIN_CONFIG_BIAS_PULL_DOWN: - if (!opt->pud_info) { - dev_err(pci->dev, - "PULL control not supported for pin %s\n", - g->name); - return -ENOTSUPP; - } - base = opt->pud_info->pullup_dn_reg_base; - add = opt->pud_info->pullup_dn_reg_add; - mask = opt->pud_info->pullup_dn_mask; - - if (param == PIN_CONFIG_BIAS_DISABLE) - rval = opt->pud_info->normal_val; - else if (param == PIN_CONFIG_BIAS_PULL_UP) - rval = opt->pud_info->pull_up_val; - else - rval = opt->pud_info->pull_dn_val; + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + param_val = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + return 0; + case PIN_CONFIG_BIAS_DISABLE: + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + if (!opt->pud_info) { + dev_err(pci->dev, + "PULL control not supported for pin %s\n", + g->name); + return -ENOTSUPP; + } + base = opt->pud_info->pullup_dn_reg_base; + add = opt->pud_info->pullup_dn_reg_add; + mask = opt->pud_info->pullup_dn_mask; + + if (param == PIN_CONFIG_BIAS_DISABLE) + rval = opt->pud_info->normal_val; + else if (param == PIN_CONFIG_BIAS_PULL_UP) + rval = opt->pud_info->pull_up_val; + else + rval = opt->pud_info->pull_dn_val; + + if (rval < 0) { + dev_err(pci->dev, + "PULL control not supported for pin %s\n", + g->name); + return -ENOTSUPP; + } + break; - if (rval < 0) { - dev_err(pci->dev, - "PULL control not supported for pin %s\n", - g->name); + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + if (!opt->od_info) { + dev_err(pci->dev, + "OD control not supported for pin %s\n", + g->name); + return -ENOTSUPP; + } + base = opt->od_info->od_reg_base; + add = opt->od_info->od_reg_add; + mask = opt->od_info->od_mask; + if (param_val == 0) + rval = opt->od_info->od_disable; + else + rval = opt->od_info->od_enable; + if (rval < 0) { + dev_err(pci->dev, + "OD control not supported for pin %s\n", + g->name); + return -ENOTSUPP; + } + break; + default: + dev_err(pci->dev, "Properties not supported\n"); return -ENOTSUPP; } - break; - case PIN_CONFIG_DRIVE_OPEN_DRAIN: - if (!opt->od_info) { - dev_err(pci->dev, - "OD control not supported for pin %s\n", - g->name); - return -ENOTSUPP; - } - base = opt->od_info->od_reg_base; - add = opt->od_info->od_reg_add; - mask = opt->od_info->od_mask; - if (param_val == 0) - rval = opt->od_info->od_disable; - else - rval = opt->od_info->od_enable; - if (rval < 0) { - dev_err(pci->dev, - "OD control not supported for pin %s\n", - g->name); - return -ENOTSUPP; + dev_dbg(pci->dev, "%s(): Add0x%02x:0x%02x:0x%02x:0x%02x\n", + __func__, base, add, mask, rval); + ret = palmas_update_bits(pci->palmas, base, add, mask, rval); + if (ret < 0) { + dev_err(pci->dev, "Reg 0x%02x update failed: %d\n", + add, ret); + return ret; } - break; - default: - dev_err(pci->dev, "Properties not supported\n"); - return -ENOTSUPP; - } + } /* for each config */ - dev_dbg(pci->dev, "%s(): Add0x%02x:0x%02x:0x%02x:0x%02x\n", - __func__, base, add, mask, rval); - ret = palmas_update_bits(pci->palmas, base, add, mask, rval); - if (ret < 0) { - dev_err(pci->dev, "Reg 0x%02x update failed: %d\n", add, ret); - return ret; - } return 0; } @@ -960,7 +969,8 @@ static int palmas_pinconf_group_get(struct pinctrl_dev *pctldev, } static int palmas_pinconf_group_set(struct pinctrl_dev *pctldev, - unsigned group, unsigned long config) + unsigned group, unsigned long *configs, + unsigned num_configs) { dev_err(pctldev->dev, "palmas_pinconf_group_set op not supported\n"); return -ENOTSUPP; diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index 51a7c24981d1..abff5895e81f 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -574,32 +574,45 @@ static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl, /* set the pin config settings for a specified pin */ static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, - unsigned long config) + unsigned long *configs, unsigned num_configs) { struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); struct rockchip_pin_bank *bank = pin_to_bank(info, pin); - enum pin_config_param param = pinconf_to_config_param(config); - u16 arg = pinconf_to_config_argument(config); - - switch (param) { - case PIN_CONFIG_BIAS_DISABLE: - return rockchip_set_pull(bank, pin - bank->pin_base, param); - break; - case PIN_CONFIG_BIAS_PULL_UP: - case PIN_CONFIG_BIAS_PULL_DOWN: - case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: - if (!rockchip_pinconf_pull_valid(info->ctrl, param)) + enum pin_config_param param; + u16 arg; + int i; + int rc; + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + rc = rockchip_set_pull(bank, pin - bank->pin_base, + param); + if (rc) + return rc; + break; + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + if (!rockchip_pinconf_pull_valid(info->ctrl, param)) + return -ENOTSUPP; + + if (!arg) + return -EINVAL; + + rc = rockchip_set_pull(bank, pin - bank->pin_base, + param); + if (rc) + return rc; + break; + default: return -ENOTSUPP; - - if (!arg) - return -EINVAL; - - return rockchip_set_pull(bank, pin - bank->pin_base, param); - break; - default: - return -ENOTSUPP; - break; - } + break; + } + } /* for each config */ return 0; } diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 439f2ef680df..222648d69f25 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -442,9 +442,17 @@ static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin, /* set the pin config settings for a specified pin */ static int samsung_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, - unsigned long config) + unsigned long *configs, unsigned num_configs) { - return samsung_pinconf_rw(pctldev, pin, &config, true); + int i, ret; + + for (i = 0; i < num_configs; i++) { + ret = samsung_pinconf_rw(pctldev, pin, &configs[i], true); + if (ret < 0) + return ret; + } /* for each config */ + + return 0; } /* get the pin config settings for a specified pin */ @@ -456,7 +464,8 @@ static int samsung_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, /* set the pin config settings for a specified pin group */ static int samsung_pinconf_group_set(struct pinctrl_dev *pctldev, - unsigned group, unsigned long config) + unsigned group, unsigned long *configs, + unsigned num_configs) { struct samsung_pinctrl_drv_data *drvdata; const unsigned int *pins; @@ -466,7 +475,7 @@ static int samsung_pinconf_group_set(struct pinctrl_dev *pctldev, pins = drvdata->pin_groups[group].pins; for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) - samsung_pinconf_set(pctldev, pins[cnt], config); + samsung_pinconf_set(pctldev, pins[cnt], configs, num_configs); return 0; } diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c index 6866548fab31..8f31768c665d 100644 --- a/drivers/pinctrl/pinctrl-single.c +++ b/drivers/pinctrl/pinctrl-single.c @@ -209,7 +209,7 @@ struct pcs_device { static int pcs_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config); static int pcs_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, - unsigned long config); + unsigned long *configs, unsigned num_configs); static enum pin_config_param pcs_bias[] = { PIN_CONFIG_BIAS_PULL_DOWN, @@ -536,7 +536,7 @@ static void pcs_pinconf_clear_bias(struct pinctrl_dev *pctldev, unsigned pin) int i; for (i = 0; i < ARRAY_SIZE(pcs_bias); i++) { config = pinconf_to_config_packed(pcs_bias[i], 0); - pcs_pinconf_set(pctldev, pin, config); + pcs_pinconf_set(pctldev, pin, &config, 1); } } @@ -622,22 +622,28 @@ static int pcs_pinconf_get(struct pinctrl_dev *pctldev, } static int pcs_pinconf_set(struct pinctrl_dev *pctldev, - unsigned pin, unsigned long config) + unsigned pin, unsigned long *configs, + unsigned num_configs) { struct pcs_device *pcs = pinctrl_dev_get_drvdata(pctldev); struct pcs_function *func; unsigned offset = 0, shift = 0, i, data, ret; u16 arg; + int j; ret = pcs_get_function(pctldev, pin, &func); if (ret) return ret; - for (i = 0; i < func->nconfs; i++) { - if (pinconf_to_config_param(config) == func->conf[i].param) { + for (j = 0; j < num_configs; j++) { + for (i = 0; i < func->nconfs; i++) { + if (pinconf_to_config_param(configs[j]) + != func->conf[i].param) + continue; + offset = pin * (pcs->width / BITS_PER_BYTE); data = pcs->read(pcs->base + offset); - arg = pinconf_to_config_argument(config); + arg = pinconf_to_config_argument(configs[j]); switch (func->conf[i].param) { /* 2 parameters */ case PIN_CONFIG_INPUT_SCHMITT: @@ -667,10 +673,14 @@ static int pcs_pinconf_set(struct pinctrl_dev *pctldev, return -ENOTSUPP; } pcs->write(data, pcs->base + offset); - return 0; + + break; } - } - return -ENOTSUPP; + if (i >= func->nconfs) + return -ENOTSUPP; + } /* for each config */ + + return 0; } static int pcs_pinconf_group_get(struct pinctrl_dev *pctldev, @@ -695,7 +705,8 @@ static int pcs_pinconf_group_get(struct pinctrl_dev *pctldev, } static int pcs_pinconf_group_set(struct pinctrl_dev *pctldev, - unsigned group, unsigned long config) + unsigned group, unsigned long *configs, + unsigned num_configs) { const unsigned *pins; unsigned npins; @@ -705,7 +716,7 @@ static int pcs_pinconf_group_set(struct pinctrl_dev *pctldev, if (ret) return ret; for (i = 0; i < npins; i++) { - if (pcs_pinconf_set(pctldev, pins[i], config)) + if (pcs_pinconf_set(pctldev, pins[i], configs, num_configs)) return -ENOTSUPP; } return 0; diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c index 2a10a318b648..9cadc68ee572 100644 --- a/drivers/pinctrl/pinctrl-st.c +++ b/drivers/pinctrl/pinctrl-st.c @@ -909,15 +909,18 @@ static void st_pinconf_set_retime(struct st_pinctrl *info, config, pin); } -static int st_pinconf_set(struct pinctrl_dev *pctldev, - unsigned pin_id, unsigned long config) +static int st_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin_id, + unsigned long *configs, unsigned num_configs) { int pin = st_gpio_pin(pin_id); struct st_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); struct st_pio_control *pc = st_get_pio_control(pctldev, pin_id); + int i; - st_pinconf_set_config(pc, pin, config); - st_pinconf_set_retime(info, pc, pin, config); + for (i = 0; i < num_configs; i++) { + st_pinconf_set_config(pc, pin, configs[i]); + st_pinconf_set_retime(info, pc, pin, configs[i]); + } /* for each config */ return 0; } diff --git a/drivers/pinctrl/pinctrl-sunxi.c b/drivers/pinctrl/pinctrl-sunxi.c index d251c76c2e62..8dbd465b01d3 100644 --- a/drivers/pinctrl/pinctrl-sunxi.c +++ b/drivers/pinctrl/pinctrl-sunxi.c @@ -274,50 +274,55 @@ static int sunxi_pconf_group_get(struct pinctrl_dev *pctldev, static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, unsigned group, - unsigned long config) + unsigned long *configs, + unsigned num_configs) { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); struct sunxi_pinctrl_group *g = &pctl->groups[group]; u32 val, mask; u16 strength; u8 dlevel; + int i; - switch (pinconf_to_config_param(config)) { - case PIN_CONFIG_DRIVE_STRENGTH: - strength = pinconf_to_config_argument(config); - if (strength > 40) - return -EINVAL; - /* - * We convert from mA to what the register expects: - * 0: 10mA - * 1: 20mA - * 2: 30mA - * 3: 40mA - */ - dlevel = strength / 10 - 1; - val = readl(pctl->membase + sunxi_dlevel_reg(g->pin)); - mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(g->pin); - writel((val & ~mask) | dlevel << sunxi_dlevel_offset(g->pin), - pctl->membase + sunxi_dlevel_reg(g->pin)); - break; - case PIN_CONFIG_BIAS_PULL_UP: - val = readl(pctl->membase + sunxi_pull_reg(g->pin)); - mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); - writel((val & ~mask) | 1 << sunxi_pull_offset(g->pin), - pctl->membase + sunxi_pull_reg(g->pin)); - break; - case PIN_CONFIG_BIAS_PULL_DOWN: - val = readl(pctl->membase + sunxi_pull_reg(g->pin)); - mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); - writel((val & ~mask) | 2 << sunxi_pull_offset(g->pin), - pctl->membase + sunxi_pull_reg(g->pin)); - break; - default: - break; - } + for (i = 0; i < num_configs; i++) { + switch (pinconf_to_config_param(configs[i])) { + case PIN_CONFIG_DRIVE_STRENGTH: + strength = pinconf_to_config_argument(configs[i]); + if (strength > 40) + return -EINVAL; + /* + * We convert from mA to what the register expects: + * 0: 10mA + * 1: 20mA + * 2: 30mA + * 3: 40mA + */ + dlevel = strength / 10 - 1; + val = readl(pctl->membase + sunxi_dlevel_reg(g->pin)); + mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(g->pin); + writel((val & ~mask) + | dlevel << sunxi_dlevel_offset(g->pin), + pctl->membase + sunxi_dlevel_reg(g->pin)); + break; + case PIN_CONFIG_BIAS_PULL_UP: + val = readl(pctl->membase + sunxi_pull_reg(g->pin)); + mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); + writel((val & ~mask) | 1 << sunxi_pull_offset(g->pin), + pctl->membase + sunxi_pull_reg(g->pin)); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + val = readl(pctl->membase + sunxi_pull_reg(g->pin)); + mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin); + writel((val & ~mask) | 2 << sunxi_pull_offset(g->pin), + pctl->membase + sunxi_pull_reg(g->pin)); + break; + default: + break; + } - /* cache the config value */ - g->config = config; + /* cache the config value */ + g->config = configs[i]; + } /* for each config */ return 0; } diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index be7e900fb298..a2e93a2b5ff4 100644 --- a/drivers/pinctrl/pinctrl-tegra.c +++ b/drivers/pinctrl/pinctrl-tegra.c @@ -436,7 +436,8 @@ static int tegra_pinconf_get(struct pinctrl_dev *pctldev, } static int tegra_pinconf_set(struct pinctrl_dev *pctldev, - unsigned pin, unsigned long config) + unsigned pin, unsigned long *configs, + unsigned num_configs) { dev_err(pctldev->dev, "pin_config_set op not supported\n"); return -ENOTSUPP; @@ -471,51 +472,57 @@ static int tegra_pinconf_group_get(struct pinctrl_dev *pctldev, } static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, - unsigned group, unsigned long config) + unsigned group, unsigned long *configs, + unsigned num_configs) { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - enum tegra_pinconf_param param = TEGRA_PINCONF_UNPACK_PARAM(config); - u16 arg = TEGRA_PINCONF_UNPACK_ARG(config); + enum tegra_pinconf_param param; + u16 arg; const struct tegra_pingroup *g; - int ret; + int ret, i; s8 bank, bit, width; s16 reg; u32 val, mask; g = &pmx->soc->groups[group]; - ret = tegra_pinconf_reg(pmx, g, param, true, &bank, ®, &bit, - &width); - if (ret < 0) - return ret; + for (i = 0; i < num_configs; i++) { + param = TEGRA_PINCONF_UNPACK_PARAM(configs[i]); + arg = TEGRA_PINCONF_UNPACK_ARG(configs[i]); - val = pmx_readl(pmx, bank, reg); + ret = tegra_pinconf_reg(pmx, g, param, true, &bank, ®, &bit, + &width); + if (ret < 0) + return ret; - /* LOCK can't be cleared */ - if (param == TEGRA_PINCONF_PARAM_LOCK) { - if ((val & BIT(bit)) && !arg) { - dev_err(pctldev->dev, "LOCK bit cannot be cleared\n"); - return -EINVAL; + val = pmx_readl(pmx, bank, reg); + + /* LOCK can't be cleared */ + if (param == TEGRA_PINCONF_PARAM_LOCK) { + if ((val & BIT(bit)) && !arg) { + dev_err(pctldev->dev, "LOCK bit cannot be cleared\n"); + return -EINVAL; + } } - } - /* Special-case Boolean values; allow any non-zero as true */ - if (width == 1) - arg = !!arg; + /* Special-case Boolean values; allow any non-zero as true */ + if (width == 1) + arg = !!arg; - /* Range-check user-supplied value */ - mask = (1 << width) - 1; - if (arg & ~mask) { - dev_err(pctldev->dev, - "config %lx: %x too big for %d bit register\n", - config, arg, width); - return -EINVAL; - } + /* Range-check user-supplied value */ + mask = (1 << width) - 1; + if (arg & ~mask) { + dev_err(pctldev->dev, + "config %lx: %x too big for %d bit register\n", + configs[i], arg, width); + return -EINVAL; + } - /* Update register */ - val &= ~(mask << bit); - val |= arg << bit; - pmx_writel(pmx, val, bank, reg); + /* Update register */ + val &= ~(mask << bit); + val |= arg << bit; + pmx_writel(pmx, val, bank, reg); + } /* for each config */ return 0; } diff --git a/drivers/pinctrl/pinctrl-tz1090-pdc.c b/drivers/pinctrl/pinctrl-tz1090-pdc.c index 494ad4b83264..5bf01c28925e 100644 --- a/drivers/pinctrl/pinctrl-tz1090-pdc.c +++ b/drivers/pinctrl/pinctrl-tz1090-pdc.c @@ -737,39 +737,46 @@ static int tz1090_pdc_pinconf_get(struct pinctrl_dev *pctldev, } static int tz1090_pdc_pinconf_set(struct pinctrl_dev *pctldev, - unsigned int pin, unsigned long config) + unsigned int pin, unsigned long *configs, + unsigned num_configs) { struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - enum pin_config_param param = pinconf_to_config_param(config); - unsigned int arg = pinconf_to_config_argument(config); + enum pin_config_param param; + unsigned int arg; int ret; u32 reg, width, mask, shift, val, tmp; unsigned long flags; + int i; - dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n", - __func__, tz1090_pdc_pins[pin].name, config); + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); - /* Get register information */ - ret = tz1090_pdc_pinconf_reg(pctldev, pin, param, true, - ®, &width, &mask, &shift, &val); - if (ret < 0) - return ret; + dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n", + __func__, tz1090_pdc_pins[pin].name, configs[i]); - /* Unpack argument and range check it */ - if (arg > 1) { - dev_dbg(pctldev->dev, "%s: arg %u out of range\n", - __func__, arg); - return -EINVAL; - } + /* Get register information */ + ret = tz1090_pdc_pinconf_reg(pctldev, pin, param, true, + ®, &width, &mask, &shift, &val); + if (ret < 0) + return ret; - /* Write register field */ - __global_lock2(flags); - tmp = pmx_read(pmx, reg); - tmp &= ~mask; - if (arg) - tmp |= val << shift; - pmx_write(pmx, tmp, reg); - __global_unlock2(flags); + /* Unpack argument and range check it */ + if (arg > 1) { + dev_dbg(pctldev->dev, "%s: arg %u out of range\n", + __func__, arg); + return -EINVAL; + } + + /* Write register field */ + __global_lock2(flags); + tmp = pmx_read(pmx, reg); + tmp &= ~mask; + if (arg) + tmp |= val << shift; + pmx_write(pmx, tmp, reg); + __global_unlock2(flags); + } /* for each config */ return 0; } @@ -860,54 +867,68 @@ static int tz1090_pdc_pinconf_group_get(struct pinctrl_dev *pctldev, static int tz1090_pdc_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned int group, - unsigned long config) + unsigned long *configs, + unsigned num_configs) { struct tz1090_pdc_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); const struct tz1090_pdc_pingroup *g = &tz1090_pdc_groups[group]; - enum pin_config_param param = pinconf_to_config_param(config); + enum pin_config_param param; const unsigned int *pit; unsigned int i; int ret, arg; u32 reg, width, mask, shift, val; unsigned long flags; const int *map; + int j; - dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n", - __func__, g->name, config); + for (j = 0; j < num_configs; j++) { + param = pinconf_to_config_param(configs[j]); - /* Get register information */ - ret = tz1090_pdc_pinconf_group_reg(pctldev, g, param, true, - ®, &width, &mask, &shift, &map); - if (ret < 0) { - /* - * Maybe we're trying to set a per-pin configuration of a group, - * so do the pins one by one. This is mainly as a convenience. - */ - for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) { - ret = tz1090_pdc_pinconf_set(pctldev, *pit, config); - if (ret) - return ret; - } - return 0; - } + dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n", + __func__, g->name, configs[j]); - /* Unpack argument and map it to register value */ - arg = pinconf_to_config_argument(config); - for (i = 0; i < BIT(width); ++i) { - if (map[i] == arg || (map[i] == -EINVAL && !arg)) { - /* Write register field */ - __global_lock2(flags); - val = pmx_read(pmx, reg); - val &= ~mask; - val |= i << shift; - pmx_write(pmx, val, reg); - __global_unlock2(flags); + /* Get register information */ + ret = tz1090_pdc_pinconf_group_reg(pctldev, g, param, true, + ®, &width, &mask, &shift, + &map); + if (ret < 0) { + /* + * Maybe we're trying to set a per-pin configuration + * of a group, so do the pins one by one. This is + * mainly as a convenience. + */ + for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) { + ret = tz1090_pdc_pinconf_set(pctldev, *pit, + configs, num_configs); + if (ret) + return ret; + } return 0; } - } - dev_dbg(pctldev->dev, "%s: arg %u not supported\n", - __func__, arg); + /* Unpack argument and map it to register value */ + arg = pinconf_to_config_argument(configs[j]); + for (i = 0; i < BIT(width); ++i) { + if (map[i] == arg || (map[i] == -EINVAL && !arg)) { + /* Write register field */ + __global_lock2(flags); + val = pmx_read(pmx, reg); + val &= ~mask; + val |= i << shift; + pmx_write(pmx, val, reg); + __global_unlock2(flags); + goto next_config; + } + } + + dev_dbg(pctldev->dev, "%s: arg %u not supported\n", + __func__, arg); + return 0; + +next_config: + ; + } /* for each config */ + return 0; } diff --git a/drivers/pinctrl/pinctrl-tz1090.c b/drivers/pinctrl/pinctrl-tz1090.c index 72d955252e41..bc9cd7a7602e 100644 --- a/drivers/pinctrl/pinctrl-tz1090.c +++ b/drivers/pinctrl/pinctrl-tz1090.c @@ -1762,39 +1762,46 @@ static int tz1090_pinconf_get(struct pinctrl_dev *pctldev, } static int tz1090_pinconf_set(struct pinctrl_dev *pctldev, - unsigned int pin, unsigned long config) + unsigned int pin, unsigned long *configs, + unsigned num_configs) { struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - enum pin_config_param param = pinconf_to_config_param(config); - unsigned int arg = pinconf_to_config_argument(config); + enum pin_config_param param; + unsigned int arg; int ret; u32 reg, width, mask, shift, val, tmp; unsigned long flags; + int i; - dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n", - __func__, tz1090_pins[pin].name, config); + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); - /* Get register information */ - ret = tz1090_pinconf_reg(pctldev, pin, param, true, - ®, &width, &mask, &shift, &val); - if (ret < 0) - return ret; + dev_dbg(pctldev->dev, "%s(pin=%s, config=%#lx)\n", + __func__, tz1090_pins[pin].name, configs[i]); - /* Unpack argument and range check it */ - if (arg > 1) { - dev_dbg(pctldev->dev, "%s: arg %u out of range\n", - __func__, arg); - return -EINVAL; - } + /* Get register information */ + ret = tz1090_pinconf_reg(pctldev, pin, param, true, + ®, &width, &mask, &shift, &val); + if (ret < 0) + return ret; - /* Write register field */ - __global_lock2(flags); - tmp = pmx_read(pmx, reg); - tmp &= ~mask; - if (arg) - tmp |= val << shift; - pmx_write(pmx, tmp, reg); - __global_unlock2(flags); + /* Unpack argument and range check it */ + if (arg > 1) { + dev_dbg(pctldev->dev, "%s: arg %u out of range\n", + __func__, arg); + return -EINVAL; + } + + /* Write register field */ + __global_lock2(flags); + tmp = pmx_read(pmx, reg); + tmp &= ~mask; + if (arg) + tmp |= val << shift; + pmx_write(pmx, tmp, reg); + __global_unlock2(flags); + } /* for each config */ return 0; } @@ -1894,68 +1901,81 @@ static int tz1090_pinconf_group_get(struct pinctrl_dev *pctldev, } static int tz1090_pinconf_group_set(struct pinctrl_dev *pctldev, - unsigned int group, unsigned long config) + unsigned int group, unsigned long *configs, + unsigned num_configs) { struct tz1090_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); const struct tz1090_pingroup *g; - enum pin_config_param param = pinconf_to_config_param(config); + enum pin_config_param param; unsigned int arg, pin, i; const unsigned int *pit; int ret; u32 reg, width, mask, shift, val; unsigned long flags; const int *map; + int j; if (group >= ARRAY_SIZE(tz1090_groups)) { pin = group - ARRAY_SIZE(tz1090_groups); - return tz1090_pinconf_set(pctldev, pin, config); + return tz1090_pinconf_set(pctldev, pin, configs, num_configs); } g = &tz1090_groups[group]; if (g->npins == 1) { pin = g->pins[0]; - ret = tz1090_pinconf_set(pctldev, pin, config); + ret = tz1090_pinconf_set(pctldev, pin, configs, num_configs); if (ret != -ENOTSUPP) return ret; } - dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n", - __func__, g->name, config); + for (j = 0; j < num_configs; j++) { + param = pinconf_to_config_param(configs[j]); - /* Get register information */ - ret = tz1090_pinconf_group_reg(pctldev, g, param, true, - ®, &width, &mask, &shift, &map); - if (ret < 0) { - /* - * Maybe we're trying to set a per-pin configuration of a group, - * so do the pins one by one. This is mainly as a convenience. - */ - for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) { - ret = tz1090_pinconf_set(pctldev, *pit, config); - if (ret) - return ret; - } - return 0; - } + dev_dbg(pctldev->dev, "%s(group=%s, config=%#lx)\n", + __func__, g->name, configs[j]); - /* Unpack argument and map it to register value */ - arg = pinconf_to_config_argument(config); - for (i = 0; i < BIT(width); ++i) { - if (map[i] == arg || (map[i] == -EINVAL && !arg)) { - /* Write register field */ - __global_lock2(flags); - val = pmx_read(pmx, reg); - val &= ~mask; - val |= i << shift; - pmx_write(pmx, val, reg); - __global_unlock2(flags); + /* Get register information */ + ret = tz1090_pinconf_group_reg(pctldev, g, param, true, ®, + &width, &mask, &shift, &map); + if (ret < 0) { + /* + * Maybe we're trying to set a per-pin configuration + * of a group, so do the pins one by one. This is + * mainly as a convenience. + */ + for (i = 0, pit = g->pins; i < g->npins; ++i, ++pit) { + ret = tz1090_pinconf_set(pctldev, *pit, configs, + num_configs); + if (ret) + return ret; + } return 0; } - } - dev_dbg(pctldev->dev, "%s: arg %u not supported\n", - __func__, arg); - return -EINVAL; + /* Unpack argument and map it to register value */ + arg = pinconf_to_config_argument(configs[j]); + for (i = 0; i < BIT(width); ++i) { + if (map[i] == arg || (map[i] == -EINVAL && !arg)) { + /* Write register field */ + __global_lock2(flags); + val = pmx_read(pmx, reg); + val &= ~mask; + val |= i << shift; + pmx_write(pmx, val, reg); + __global_unlock2(flags); + goto next_config; + } + } + + dev_dbg(pctldev->dev, "%s: arg %u not supported\n", + __func__, arg); + return -EINVAL; + +next_config: + ; + } /* for each config */ + + return 0; } static struct pinconf_ops tz1090_pinconf_ops = { diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c index 6fbdc06d2998..209a01b8bd3b 100644 --- a/drivers/pinctrl/pinctrl-u300.c +++ b/drivers/pinctrl/pinctrl-u300.c @@ -1027,21 +1027,23 @@ static int u300_pin_config_get(struct pinctrl_dev *pctldev, unsigned pin, } static int u300_pin_config_set(struct pinctrl_dev *pctldev, unsigned pin, - unsigned long config) + unsigned long *configs, unsigned num_configs) { struct pinctrl_gpio_range *range = pinctrl_find_gpio_range_from_pin(pctldev, pin); - int ret; + int ret, i; if (!range) return -EINVAL; - /* Note: none of these configurations take any argument */ - ret = u300_gpio_config_set(range->gc, - (pin - range->pin_base + range->base), - pinconf_to_config_param(config)); - if (ret) - return ret; + for (i = 0; i < num_configs; i++) { + /* Note: none of these configurations take any argument */ + ret = u300_gpio_config_set(range->gc, + (pin - range->pin_base + range->base), + pinconf_to_config_param(configs[i])); + if (ret) + return ret; + } /* for each config */ return 0; } diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c index 86c8cf82105b..ed2d1ba69cef 100644 --- a/drivers/pinctrl/pinctrl-xway.c +++ b/drivers/pinctrl/pinctrl-xway.c @@ -499,74 +499,101 @@ static int xway_pinconf_get(struct pinctrl_dev *pctldev, static int xway_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, - unsigned long config) + unsigned long *configs, + unsigned num_configs) { struct ltq_pinmux_info *info = pinctrl_dev_get_drvdata(pctldev); - enum ltq_pinconf_param param = LTQ_PINCONF_UNPACK_PARAM(config); - int arg = LTQ_PINCONF_UNPACK_ARG(config); + enum ltq_pinconf_param param; + int arg; int port = PORT(pin); u32 reg; + int i; + + for (i = 0; i < num_configs; i++) { + param = LTQ_PINCONF_UNPACK_PARAM(configs[i]); + arg = LTQ_PINCONF_UNPACK_ARG(configs[i]); + + switch (param) { + case LTQ_PINCONF_PARAM_OPEN_DRAIN: + if (port == PORT3) + reg = GPIO3_OD; + else + reg = GPIO_OD(pin); + if (arg == 0) + gpio_setbit(info->membase[0], + reg, + PORT_PIN(pin)); + else + gpio_clearbit(info->membase[0], + reg, + PORT_PIN(pin)); + break; - switch (param) { - case LTQ_PINCONF_PARAM_OPEN_DRAIN: - if (port == PORT3) - reg = GPIO3_OD; - else - reg = GPIO_OD(pin); - if (arg == 0) + case LTQ_PINCONF_PARAM_PULL: + if (port == PORT3) + reg = GPIO3_PUDEN; + else + reg = GPIO_PUDEN(pin); + if (arg == 0) { + gpio_clearbit(info->membase[0], + reg, + PORT_PIN(pin)); + break; + } gpio_setbit(info->membase[0], reg, PORT_PIN(pin)); - else - gpio_clearbit(info->membase[0], reg, PORT_PIN(pin)); - break; - case LTQ_PINCONF_PARAM_PULL: - if (port == PORT3) - reg = GPIO3_PUDEN; - else - reg = GPIO_PUDEN(pin); - if (arg == 0) { - gpio_clearbit(info->membase[0], reg, PORT_PIN(pin)); + if (port == PORT3) + reg = GPIO3_PUDSEL; + else + reg = GPIO_PUDSEL(pin); + if (arg == 1) + gpio_clearbit(info->membase[0], + reg, + PORT_PIN(pin)); + else if (arg == 2) + gpio_setbit(info->membase[0], + reg, + PORT_PIN(pin)); + else + dev_err(pctldev->dev, + "Invalid pull value %d\n", arg); break; - } - gpio_setbit(info->membase[0], reg, PORT_PIN(pin)); - if (port == PORT3) - reg = GPIO3_PUDSEL; - else - reg = GPIO_PUDSEL(pin); - if (arg == 1) - gpio_clearbit(info->membase[0], reg, PORT_PIN(pin)); - else if (arg == 2) - gpio_setbit(info->membase[0], reg, PORT_PIN(pin)); - else - dev_err(pctldev->dev, "Invalid pull value %d\n", arg); - break; + case LTQ_PINCONF_PARAM_OUTPUT: + reg = GPIO_DIR(pin); + if (arg == 0) + gpio_clearbit(info->membase[0], + reg, + PORT_PIN(pin)); + else + gpio_setbit(info->membase[0], + reg, + PORT_PIN(pin)); + break; - case LTQ_PINCONF_PARAM_OUTPUT: - reg = GPIO_DIR(pin); - if (arg == 0) - gpio_clearbit(info->membase[0], reg, PORT_PIN(pin)); - else - gpio_setbit(info->membase[0], reg, PORT_PIN(pin)); - break; + default: + dev_err(pctldev->dev, + "Invalid config param %04x\n", param); + return -ENOTSUPP; + } + } /* for each config */ - default: - dev_err(pctldev->dev, "Invalid config param %04x\n", param); - return -ENOTSUPP; - } return 0; } int xway_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned selector, - unsigned long config) + unsigned long *configs, + unsigned num_configs) { struct ltq_pinmux_info *info = pinctrl_dev_get_drvdata(pctldev); int i, ret = 0; for (i = 0; i < info->grps[selector].npins && !ret; i++) ret = xway_pinconf_set(pctldev, - info->grps[selector].pins[i], config); + info->grps[selector].pins[i], + configs, + num_configs); return ret; } diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c index 8649ec3910a3..e758af95c209 100644 --- a/drivers/pinctrl/sh-pfc/pinctrl.c +++ b/drivers/pinctrl/sh-pfc/pinctrl.c @@ -529,38 +529,44 @@ static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned _pin, } static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned _pin, - unsigned long config) + unsigned long *configs, unsigned num_configs) { struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); struct sh_pfc *pfc = pmx->pfc; - enum pin_config_param param = pinconf_to_config_param(config); + enum pin_config_param param; unsigned long flags; + unsigned int i; - if (!sh_pfc_pinconf_validate(pfc, _pin, param)) - return -ENOTSUPP; + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); - switch (param) { - case PIN_CONFIG_BIAS_PULL_UP: - case PIN_CONFIG_BIAS_PULL_DOWN: - case PIN_CONFIG_BIAS_DISABLE: - if (!pfc->info->ops || !pfc->info->ops->set_bias) + if (!sh_pfc_pinconf_validate(pfc, _pin, param)) return -ENOTSUPP; - spin_lock_irqsave(&pfc->lock, flags); - pfc->info->ops->set_bias(pfc, _pin, param); - spin_unlock_irqrestore(&pfc->lock, flags); + switch (param) { + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_DISABLE: + if (!pfc->info->ops || !pfc->info->ops->set_bias) + return -ENOTSUPP; - break; + spin_lock_irqsave(&pfc->lock, flags); + pfc->info->ops->set_bias(pfc, _pin, param); + spin_unlock_irqrestore(&pfc->lock, flags); - default: - return -ENOTSUPP; - } + break; + + default: + return -ENOTSUPP; + } + } /* for each config */ return 0; } static int sh_pfc_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned group, - unsigned long config) + unsigned long *configs, + unsigned num_configs) { struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); const unsigned int *pins; @@ -571,7 +577,7 @@ static int sh_pfc_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned group, num_pins = pmx->pfc->info->groups[group].nr_pins; for (i = 0; i < num_pins; ++i) - sh_pfc_pinconf_set(pctldev, pins[i], config); + sh_pfc_pinconf_set(pctldev, pins[i], configs, num_configs); return 0; } diff --git a/drivers/pinctrl/vt8500/pinctrl-wmt.c b/drivers/pinctrl/vt8500/pinctrl-wmt.c index 0cc4335bc0f2..39aec0850810 100644 --- a/drivers/pinctrl/vt8500/pinctrl-wmt.c +++ b/drivers/pinctrl/vt8500/pinctrl-wmt.c @@ -424,15 +424,16 @@ static int wmt_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, } static int wmt_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, - unsigned long config) + unsigned long *configs, unsigned num_configs) { struct wmt_pinctrl_data *data = pinctrl_dev_get_drvdata(pctldev); - enum pin_config_param param = pinconf_to_config_param(config); - u16 arg = pinconf_to_config_argument(config); + enum pin_config_param param; + u16 arg; u32 bank = WMT_BANK_FROM_PIN(pin); u32 bit = WMT_BIT_FROM_PIN(pin); u32 reg_pull_en = data->banks[bank].reg_pull_en; u32 reg_pull_cfg = data->banks[bank].reg_pull_cfg; + int i; if ((reg_pull_en == NO_REG) || (reg_pull_cfg == NO_REG)) { dev_err(data->dev, "bias functions not supported on pin %d\n", @@ -440,28 +441,33 @@ static int wmt_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, return -EINVAL; } - if ((param == PIN_CONFIG_BIAS_PULL_DOWN) || - (param == PIN_CONFIG_BIAS_PULL_UP)) { - if (arg == 0) - param = PIN_CONFIG_BIAS_DISABLE; - } + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); - switch (param) { - case PIN_CONFIG_BIAS_DISABLE: - wmt_clearbits(data, reg_pull_en, BIT(bit)); - break; - case PIN_CONFIG_BIAS_PULL_DOWN: - wmt_clearbits(data, reg_pull_cfg, BIT(bit)); - wmt_setbits(data, reg_pull_en, BIT(bit)); - break; - case PIN_CONFIG_BIAS_PULL_UP: - wmt_setbits(data, reg_pull_cfg, BIT(bit)); - wmt_setbits(data, reg_pull_en, BIT(bit)); - break; - default: - dev_err(data->dev, "unknown pinconf param\n"); - return -EINVAL; - } + if ((param == PIN_CONFIG_BIAS_PULL_DOWN) || + (param == PIN_CONFIG_BIAS_PULL_UP)) { + if (arg == 0) + param = PIN_CONFIG_BIAS_DISABLE; + } + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + wmt_clearbits(data, reg_pull_en, BIT(bit)); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + wmt_clearbits(data, reg_pull_cfg, BIT(bit)); + wmt_setbits(data, reg_pull_en, BIT(bit)); + break; + case PIN_CONFIG_BIAS_PULL_UP: + wmt_setbits(data, reg_pull_cfg, BIT(bit)); + wmt_setbits(data, reg_pull_en, BIT(bit)); + break; + default: + dev_err(data->dev, "unknown pinconf param\n"); + return -EINVAL; + } + } /* for each config */ return 0; } diff --git a/include/linux/pinctrl/pinconf.h b/include/linux/pinctrl/pinconf.h index f6998692bdc9..09eb80f2574a 100644 --- a/include/linux/pinctrl/pinconf.h +++ b/include/linux/pinctrl/pinconf.h @@ -47,13 +47,15 @@ struct pinconf_ops { unsigned long *config); int (*pin_config_set) (struct pinctrl_dev *pctldev, unsigned pin, - unsigned long config); + unsigned long *configs, + unsigned num_configs); int (*pin_config_group_get) (struct pinctrl_dev *pctldev, unsigned selector, unsigned long *config); int (*pin_config_group_set) (struct pinctrl_dev *pctldev, unsigned selector, - unsigned long config); + unsigned long *configs, + unsigned num_configs); int (*pin_config_dbg_parse_modify) (struct pinctrl_dev *pctldev, const char *arg, unsigned long *config); -- cgit v1.2.3-71-gd317 From 574979c617eb9593f8dfbb804da3f0e00f8bf28e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 28 Aug 2013 09:51:41 -0700 Subject: sysfs: sysfs_create_groups returns a value. When I included the "empty" function for sysfs_create_groups() when CONFIG_SYSFS=n, I forgot to return a value for it, so things blew up the build. This patch fixes that, stupid me. Reported-by: kbuild test robot Signed-off-by: Greg Kroah-Hartman --- include/linux/sysfs.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index e647a1aa0723..11baec7c9b26 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -353,6 +353,7 @@ static inline int sysfs_create_group(struct kobject *kobj, static inline int sysfs_create_groups(struct kobject *kobj, const struct attribute_group **groups) { + return 0; } static inline int sysfs_update_group(struct kobject *kobj, -- cgit v1.2.3-71-gd317 From 319a441d1361ea703b091caf92418f8121eadfc5 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Wed, 7 Aug 2013 14:01:59 +0300 Subject: IB/core: Add receive flow steering support The RDMA stack allows for applications to create IB_QPT_RAW_PACKET QPs, which receive plain Ethernet packets, specifically packets that don't carry any QPN to be matched by the receiving side. Applications using these QPs must be provided with a method to program some steering rule with the HW so packets arriving at the local port can be routed to them. This patch adds ib_create_flow(), which allow providing a flow specification for a QP. When there's a match between the specification and a received packet, the packet is forwarded to that QP, in a the same way one uses ib_attach_multicast() for IB UD multicast handling. Flow specifications are provided as instances of struct ib_flow_spec_yyy, which describe L2, L3 and L4 headers. Currently specs for Ethernet, IPv4, TCP and UDP are defined. Flow specs are made of values and masks. The input to ib_create_flow() is a struct ib_flow_attr, which contains a few mandatory control elements and optional flow specs. struct ib_flow_attr { enum ib_flow_attr_type type; u16 size; u16 priority; u32 flags; u8 num_of_specs; u8 port; /* Following are the optional layers according to user request * struct ib_flow_spec_yyy * struct ib_flow_spec_zzz */ }; As these specs are eventually coming from user space, they are defined and used in a way which allows adding new spec types without kernel/user ABI change, just with a little API enhancement which defines the newly added spec. The flow spec structures are defined with TLV (Type-Length-Value) entries, which allows calling ib_create_flow() with a list of variable length of optional specs. For the actual processing of ib_flow_attr the driver uses the number of specs and the size mandatory fields along with the TLV nature of the specs. Steering rules processing order is according to the domain over which the rule is set and the rule priority. All rules set by user space applicatations fall into the IB_FLOW_DOMAIN_USER domain, other domains could be used by future IPoIB RFS and Ethetool flow-steering interface implementation. Lower numerical value for the priority field means higher priority. The returned value from ib_create_flow() is a struct ib_flow, which contains a database pointer (handle) provided by the HW driver to be used when calling ib_destroy_flow(). Applications that offload TCP/IP traffic can also be written over IB UD QPs. The ib_create_flow() / ib_destroy_flow() API is designed to support UD QPs too. A HW driver can set IB_DEVICE_MANAGED_FLOW_STEERING to denote support for flow steering. The ib_flow_attr enum type supports usage of flow steering for promiscuous and sniffer purposes: IB_FLOW_ATTR_NORMAL - "regular" rule, steering according to rule specification IB_FLOW_ATTR_ALL_DEFAULT - default unicast and multicast rule, receive all Ethernet traffic which isn't steered to any QP IB_FLOW_ATTR_MC_DEFAULT - same as IB_FLOW_ATTR_ALL_DEFAULT but only for multicast IB_FLOW_ATTR_SNIFFER - sniffer rule, receive all port traffic ALL_DEFAULT and MC_DEFAULT rules options are valid only for Ethernet link type. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/core/verbs.c | 27 +++++++++ include/rdma/ib_verbs.h | 119 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 144 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 22192deb8828..87a8102c9dbe 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -1254,3 +1254,30 @@ int ib_dealloc_xrcd(struct ib_xrcd *xrcd) return xrcd->device->dealloc_xrcd(xrcd); } EXPORT_SYMBOL(ib_dealloc_xrcd); + +struct ib_flow *ib_create_flow(struct ib_qp *qp, + struct ib_flow_attr *flow_attr, + int domain) +{ + struct ib_flow *flow_id; + if (!qp->device->create_flow) + return ERR_PTR(-ENOSYS); + + flow_id = qp->device->create_flow(qp, flow_attr, domain); + if (!IS_ERR(flow_id)) + atomic_inc(&qp->usecnt); + return flow_id; +} +EXPORT_SYMBOL(ib_create_flow); + +int ib_destroy_flow(struct ib_flow *flow_id) +{ + int err; + struct ib_qp *qp = flow_id->qp; + + err = qp->device->destroy_flow(flow_id); + if (!err) + atomic_dec(&qp->usecnt); + return err; +} +EXPORT_SYMBOL(ib_destroy_flow); diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 645c3cedce9c..6f874b00491a 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -116,7 +116,8 @@ enum ib_device_cap_flags { IB_DEVICE_MEM_MGT_EXTENSIONS = (1<<21), IB_DEVICE_BLOCK_MULTICAST_LOOPBACK = (1<<22), IB_DEVICE_MEM_WINDOW_TYPE_2A = (1<<23), - IB_DEVICE_MEM_WINDOW_TYPE_2B = (1<<24) + IB_DEVICE_MEM_WINDOW_TYPE_2B = (1<<24), + IB_DEVICE_MANAGED_FLOW_STEERING = (1<<29) }; enum ib_atomic_cap { @@ -1033,7 +1034,8 @@ struct ib_qp { struct ib_srq *srq; struct ib_xrcd *xrcd; /* XRC TGT QPs only */ struct list_head xrcd_list; - atomic_t usecnt; /* count times opened, mcast attaches */ + /* count times opened, mcast attaches, flow attaches */ + atomic_t usecnt; struct list_head open_list; struct ib_qp *real_qp; struct ib_uobject *uobject; @@ -1068,6 +1070,110 @@ struct ib_fmr { u32 rkey; }; +/* Supported steering options */ +enum ib_flow_attr_type { + /* steering according to rule specifications */ + IB_FLOW_ATTR_NORMAL = 0x0, + /* default unicast and multicast rule - + * receive all Eth traffic which isn't steered to any QP + */ + IB_FLOW_ATTR_ALL_DEFAULT = 0x1, + /* default multicast rule - + * receive all Eth multicast traffic which isn't steered to any QP + */ + IB_FLOW_ATTR_MC_DEFAULT = 0x2, + /* sniffer rule - receive all port traffic */ + IB_FLOW_ATTR_SNIFFER = 0x3 +}; + +/* Supported steering header types */ +enum ib_flow_spec_type { + /* L2 headers*/ + IB_FLOW_SPEC_ETH = 0x20, + /* L3 header*/ + IB_FLOW_SPEC_IPV4 = 0x30, + /* L4 headers*/ + IB_FLOW_SPEC_TCP = 0x40, + IB_FLOW_SPEC_UDP = 0x41 +}; + +/* Flow steering rule priority is set according to it's domain. + * Lower domain value means higher priority. + */ +enum ib_flow_domain { + IB_FLOW_DOMAIN_USER, + IB_FLOW_DOMAIN_ETHTOOL, + IB_FLOW_DOMAIN_RFS, + IB_FLOW_DOMAIN_NIC, + IB_FLOW_DOMAIN_NUM /* Must be last */ +}; + +struct ib_flow_eth_filter { + u8 dst_mac[6]; + u8 src_mac[6]; + __be16 ether_type; + __be16 vlan_tag; +}; + +struct ib_flow_spec_eth { + enum ib_flow_spec_type type; + u16 size; + struct ib_flow_eth_filter val; + struct ib_flow_eth_filter mask; +}; + +struct ib_flow_ipv4_filter { + __be32 src_ip; + __be32 dst_ip; +}; + +struct ib_flow_spec_ipv4 { + enum ib_flow_spec_type type; + u16 size; + struct ib_flow_ipv4_filter val; + struct ib_flow_ipv4_filter mask; +}; + +struct ib_flow_tcp_udp_filter { + __be16 dst_port; + __be16 src_port; +}; + +struct ib_flow_spec_tcp_udp { + enum ib_flow_spec_type type; + u16 size; + struct ib_flow_tcp_udp_filter val; + struct ib_flow_tcp_udp_filter mask; +}; + +union ib_flow_spec { + struct { + enum ib_flow_spec_type type; + u16 size; + }; + struct ib_flow_spec_eth eth; + struct ib_flow_spec_ipv4 ipv4; + struct ib_flow_spec_tcp_udp tcp_udp; +}; + +struct ib_flow_attr { + enum ib_flow_attr_type type; + u16 size; + u16 priority; + u32 flags; + u8 num_of_specs; + u8 port; + /* Following are the optional layers according to user request + * struct ib_flow_spec_xxx + * struct ib_flow_spec_yyy + */ +}; + +struct ib_flow { + struct ib_qp *qp; + struct ib_uobject *uobject; +}; + struct ib_mad; struct ib_grh; @@ -1300,6 +1406,11 @@ struct ib_device { struct ib_ucontext *ucontext, struct ib_udata *udata); int (*dealloc_xrcd)(struct ib_xrcd *xrcd); + struct ib_flow * (*create_flow)(struct ib_qp *qp, + struct ib_flow_attr + *flow_attr, + int domain); + int (*destroy_flow)(struct ib_flow *flow_id); struct ib_dma_mapping_ops *dma_ops; @@ -2260,4 +2371,8 @@ struct ib_xrcd *ib_alloc_xrcd(struct ib_device *device); */ int ib_dealloc_xrcd(struct ib_xrcd *xrcd); +struct ib_flow *ib_create_flow(struct ib_qp *qp, + struct ib_flow_attr *flow_attr, int domain); +int ib_destroy_flow(struct ib_flow *flow_id); + #endif /* IB_VERBS_H */ -- cgit v1.2.3-71-gd317 From 400dbc96583ff3b8ad4c09bd7e9dcd35a6215922 Mon Sep 17 00:00:00 2001 From: Igor Ivanov Date: Wed, 14 Aug 2013 13:58:29 +0300 Subject: IB/core: Infrastructure for extensible uverbs commands Add infrastructure to support extended uverbs capabilities in a forward/backward manner. Uverbs command opcodes which are based on the verbs extensions approach should be greater or equal to IB_USER_VERBS_CMD_THRESHOLD. They have new header format and processed a bit differently. Whenever a specific IB_USER_VERBS_CMD_XXX is extended, which practically means it needs to have additional arguments, we will be able to add them without creating a completely new IB_USER_VERBS_CMD_YYY command or bumping the uverbs ABI version. This patch for itself doesn't provide the whole scheme which is also dependent on adding a comp_mask field to each extended uverbs command struct. The new header framework allows for future extension of the CMD arguments (ib_uverbs_cmd_hdr.in_words, ib_uverbs_cmd_hdr.out_words) for an existing new command (that is a command that supports the new uverbs command header format suggested in this patch) w/o bumping ABI version and with maintaining backward and formward compatibility to new and old libibverbs versions. In the uverbs command we are passing both uverbs arguments and the provider arguments. We split the ib_uverbs_cmd_hdr.in_words to ib_uverbs_cmd_hdr.in_words which will now carry only uverbs input argument struct size and ib_uverbs_cmd_hdr.provider_in_words that will carry the provider input argument size. Same goes for the response (the uverbs CMD output argument). For example take the create_cq call and the mlx4_ib provider: The uverbs layer gets libibverb's struct ibv_create_cq (named struct ib_uverbs_create_cq in the kernel), mlx4_ib gets libmlx4's struct mlx4_create_cq (which includes struct ibv_create_cq and is named struct mlx4_ib_create_cq in the kernel) and in_words = sizeof(mlx4_create_cq)/4 . Thus ib_uverbs_cmd_hdr.in_words carry both uverbs plus mlx4_ib input argument sizes, where uverbs assumes it knows the size of its input argument - struct ibv_create_cq. Now, if we wish to add a variable to struct ibv_create_cq, we can add a comp_mask field to the struct which is basically bit field indicating which fields exists in the struct (as done for the libibverbs API extension), but we need a way to tell what is the total size of the struct and not assume the struct size is predefined (since we may get different struct sizes from different user libibverbs versions). So we know at which point the provider input argument (struct mlx4_create_cq) begins. Same goes for extending the provider struct mlx4_create_cq. Thus we split the ib_uverbs_cmd_hdr.in_words to ib_uverbs_cmd_hdr.in_words which will now carry only uverbs input argument struct size and ib_uverbs_cmd_hdr.provider_in_words that will carry the provider (mlx4_ib) input argument size. Signed-off-by: Igor Ivanov Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/core/uverbs_main.c | 29 ++++++++++++++++++++++++----- include/uapi/rdma/ib_user_verbs.h | 10 ++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 2c6f0f2ecd9d..e4e7b2449d19 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -583,9 +583,6 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, if (copy_from_user(&hdr, buf, sizeof hdr)) return -EFAULT; - if (hdr.in_words * 4 != count) - return -EINVAL; - if (hdr.command >= ARRAY_SIZE(uverbs_cmd_table) || !uverbs_cmd_table[hdr.command]) return -EINVAL; @@ -597,8 +594,30 @@ static ssize_t ib_uverbs_write(struct file *filp, const char __user *buf, if (!(file->device->ib_dev->uverbs_cmd_mask & (1ull << hdr.command))) return -ENOSYS; - return uverbs_cmd_table[hdr.command](file, buf + sizeof hdr, - hdr.in_words * 4, hdr.out_words * 4); + if (hdr.command >= IB_USER_VERBS_CMD_THRESHOLD) { + struct ib_uverbs_cmd_hdr_ex hdr_ex; + + if (copy_from_user(&hdr_ex, buf, sizeof(hdr_ex))) + return -EFAULT; + + if (((hdr_ex.in_words + hdr_ex.provider_in_words) * 4) != count) + return -EINVAL; + + return uverbs_cmd_table[hdr.command](file, + buf + sizeof(hdr_ex), + (hdr_ex.in_words + + hdr_ex.provider_in_words) * 4, + (hdr_ex.out_words + + hdr_ex.provider_out_words) * 4); + } else { + if (hdr.in_words * 4 != count) + return -EINVAL; + + return uverbs_cmd_table[hdr.command](file, + buf + sizeof(hdr), + hdr.in_words * 4, + hdr.out_words * 4); + } } static int ib_uverbs_mmap(struct file *filp, struct vm_area_struct *vma) diff --git a/include/uapi/rdma/ib_user_verbs.h b/include/uapi/rdma/ib_user_verbs.h index 805711ea2005..61535aa0a62e 100644 --- a/include/uapi/rdma/ib_user_verbs.h +++ b/include/uapi/rdma/ib_user_verbs.h @@ -43,6 +43,7 @@ * compatibility are made. */ #define IB_USER_VERBS_ABI_VERSION 6 +#define IB_USER_VERBS_CMD_THRESHOLD 50 enum { IB_USER_VERBS_CMD_GET_CONTEXT, @@ -123,6 +124,15 @@ struct ib_uverbs_cmd_hdr { __u16 out_words; }; +struct ib_uverbs_cmd_hdr_ex { + __u32 command; + __u16 in_words; + __u16 out_words; + __u16 provider_in_words; + __u16 provider_out_words; + __u32 cmd_hdr_reserved; +}; + struct ib_uverbs_get_context { __u64 response; __u64 driver_data[0]; -- cgit v1.2.3-71-gd317 From 436f2ad05a0b65b1467ddf51bc68171c381bf844 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Wed, 14 Aug 2013 13:58:30 +0300 Subject: IB/core: Export ib_create/destroy_flow through uverbs Implement ib_uverbs_create_flow() and ib_uverbs_destroy_flow() to support flow steering for user space applications. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/core/uverbs.h | 3 + drivers/infiniband/core/uverbs_cmd.c | 214 ++++++++++++++++++++++++++++++++++ drivers/infiniband/core/uverbs_main.c | 13 ++- include/rdma/ib_verbs.h | 1 + include/uapi/rdma/ib_user_verbs.h | 89 +++++++++++++- 5 files changed, 318 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h index 0fcd7aa26fa2..ad9d1028102d 100644 --- a/drivers/infiniband/core/uverbs.h +++ b/drivers/infiniband/core/uverbs.h @@ -155,6 +155,7 @@ extern struct idr ib_uverbs_cq_idr; extern struct idr ib_uverbs_qp_idr; extern struct idr ib_uverbs_srq_idr; extern struct idr ib_uverbs_xrcd_idr; +extern struct idr ib_uverbs_rule_idr; void idr_remove_uobj(struct idr *idp, struct ib_uobject *uobj); @@ -215,5 +216,7 @@ IB_UVERBS_DECLARE_CMD(destroy_srq); IB_UVERBS_DECLARE_CMD(create_xsrq); IB_UVERBS_DECLARE_CMD(open_xrcd); IB_UVERBS_DECLARE_CMD(close_xrcd); +IB_UVERBS_DECLARE_CMD(create_flow); +IB_UVERBS_DECLARE_CMD(destroy_flow); #endif /* UVERBS_H */ diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index b3c07b0c9f26..6e98df929e29 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -54,6 +54,7 @@ static struct uverbs_lock_class qp_lock_class = { .name = "QP-uobj" }; static struct uverbs_lock_class ah_lock_class = { .name = "AH-uobj" }; static struct uverbs_lock_class srq_lock_class = { .name = "SRQ-uobj" }; static struct uverbs_lock_class xrcd_lock_class = { .name = "XRCD-uobj" }; +static struct uverbs_lock_class rule_lock_class = { .name = "RULE-uobj" }; #define INIT_UDATA(udata, ibuf, obuf, ilen, olen) \ do { \ @@ -330,6 +331,7 @@ ssize_t ib_uverbs_get_context(struct ib_uverbs_file *file, INIT_LIST_HEAD(&ucontext->srq_list); INIT_LIST_HEAD(&ucontext->ah_list); INIT_LIST_HEAD(&ucontext->xrcd_list); + INIT_LIST_HEAD(&ucontext->rule_list); ucontext->closing = 0; resp.num_comp_vectors = file->device->num_comp_vectors; @@ -2587,6 +2589,218 @@ out_put: return ret ? ret : in_len; } +static int kern_spec_to_ib_spec(struct ib_kern_spec *kern_spec, + union ib_flow_spec *ib_spec) +{ + ib_spec->type = kern_spec->type; + + switch (ib_spec->type) { + case IB_FLOW_SPEC_ETH: + ib_spec->eth.size = sizeof(struct ib_flow_spec_eth); + if (ib_spec->eth.size != kern_spec->eth.size) + return -EINVAL; + memcpy(&ib_spec->eth.val, &kern_spec->eth.val, + sizeof(struct ib_flow_eth_filter)); + memcpy(&ib_spec->eth.mask, &kern_spec->eth.mask, + sizeof(struct ib_flow_eth_filter)); + break; + case IB_FLOW_SPEC_IPV4: + ib_spec->ipv4.size = sizeof(struct ib_flow_spec_ipv4); + if (ib_spec->ipv4.size != kern_spec->ipv4.size) + return -EINVAL; + memcpy(&ib_spec->ipv4.val, &kern_spec->ipv4.val, + sizeof(struct ib_flow_ipv4_filter)); + memcpy(&ib_spec->ipv4.mask, &kern_spec->ipv4.mask, + sizeof(struct ib_flow_ipv4_filter)); + break; + case IB_FLOW_SPEC_TCP: + case IB_FLOW_SPEC_UDP: + ib_spec->tcp_udp.size = sizeof(struct ib_flow_spec_tcp_udp); + if (ib_spec->tcp_udp.size != kern_spec->tcp_udp.size) + return -EINVAL; + memcpy(&ib_spec->tcp_udp.val, &kern_spec->tcp_udp.val, + sizeof(struct ib_flow_tcp_udp_filter)); + memcpy(&ib_spec->tcp_udp.mask, &kern_spec->tcp_udp.mask, + sizeof(struct ib_flow_tcp_udp_filter)); + break; + default: + return -EINVAL; + } + return 0; +} + +ssize_t ib_uverbs_create_flow(struct ib_uverbs_file *file, + const char __user *buf, int in_len, + int out_len) +{ + struct ib_uverbs_create_flow cmd; + struct ib_uverbs_create_flow_resp resp; + struct ib_uobject *uobj; + struct ib_flow *flow_id; + struct ib_kern_flow_attr *kern_flow_attr; + struct ib_flow_attr *flow_attr; + struct ib_qp *qp; + int err = 0; + void *kern_spec; + void *ib_spec; + int i; + int kern_attr_size; + + if (out_len < sizeof(resp)) + return -ENOSPC; + + if (copy_from_user(&cmd, buf, sizeof(cmd))) + return -EFAULT; + + if ((cmd.flow_attr.type == IB_FLOW_ATTR_SNIFFER && + !capable(CAP_NET_ADMIN)) || !capable(CAP_NET_RAW)) + return -EPERM; + + if (cmd.flow_attr.num_of_specs) { + kern_flow_attr = kmalloc(cmd.flow_attr.size, GFP_KERNEL); + if (!kern_flow_attr) + return -ENOMEM; + + memcpy(kern_flow_attr, &cmd.flow_attr, sizeof(*kern_flow_attr)); + kern_attr_size = cmd.flow_attr.size - sizeof(cmd) - sizeof(struct ib_uverbs_cmd_hdr_ex); + if (copy_from_user(kern_flow_attr + 1, buf + sizeof(cmd), + kern_attr_size)) { + err = -EFAULT; + goto err_free_attr; + } + } else { + kern_flow_attr = &cmd.flow_attr; + kern_attr_size = sizeof(cmd.flow_attr); + } + + uobj = kmalloc(sizeof(*uobj), GFP_KERNEL); + if (!uobj) { + err = -ENOMEM; + goto err_free_attr; + } + init_uobj(uobj, 0, file->ucontext, &rule_lock_class); + down_write(&uobj->mutex); + + qp = idr_read_qp(cmd.qp_handle, file->ucontext); + if (!qp) { + err = -EINVAL; + goto err_uobj; + } + + flow_attr = kmalloc(cmd.flow_attr.size, GFP_KERNEL); + if (!flow_attr) { + err = -ENOMEM; + goto err_put; + } + + flow_attr->type = kern_flow_attr->type; + flow_attr->priority = kern_flow_attr->priority; + flow_attr->num_of_specs = kern_flow_attr->num_of_specs; + flow_attr->port = kern_flow_attr->port; + flow_attr->flags = kern_flow_attr->flags; + flow_attr->size = sizeof(*flow_attr); + + kern_spec = kern_flow_attr + 1; + ib_spec = flow_attr + 1; + for (i = 0; i < flow_attr->num_of_specs && kern_attr_size > 0; i++) { + err = kern_spec_to_ib_spec(kern_spec, ib_spec); + if (err) + goto err_free; + flow_attr->size += + ((union ib_flow_spec *) ib_spec)->size; + kern_attr_size -= ((struct ib_kern_spec *) kern_spec)->size; + kern_spec += ((struct ib_kern_spec *) kern_spec)->size; + ib_spec += ((union ib_flow_spec *) ib_spec)->size; + } + if (kern_attr_size) { + pr_warn("create flow failed, %d bytes left from uverb cmd\n", + kern_attr_size); + goto err_free; + } + flow_id = ib_create_flow(qp, flow_attr, IB_FLOW_DOMAIN_USER); + if (IS_ERR(flow_id)) { + err = PTR_ERR(flow_id); + goto err_free; + } + flow_id->qp = qp; + flow_id->uobject = uobj; + uobj->object = flow_id; + + err = idr_add_uobj(&ib_uverbs_rule_idr, uobj); + if (err) + goto destroy_flow; + + memset(&resp, 0, sizeof(resp)); + resp.flow_handle = uobj->id; + + if (copy_to_user((void __user *)(unsigned long) cmd.response, + &resp, sizeof(resp))) { + err = -EFAULT; + goto err_copy; + } + + put_qp_read(qp); + mutex_lock(&file->mutex); + list_add_tail(&uobj->list, &file->ucontext->rule_list); + mutex_unlock(&file->mutex); + + uobj->live = 1; + + up_write(&uobj->mutex); + kfree(flow_attr); + if (cmd.flow_attr.num_of_specs) + kfree(kern_flow_attr); + return in_len; +err_copy: + idr_remove_uobj(&ib_uverbs_rule_idr, uobj); +destroy_flow: + ib_destroy_flow(flow_id); +err_free: + kfree(flow_attr); +err_put: + put_qp_read(qp); +err_uobj: + put_uobj_write(uobj); +err_free_attr: + if (cmd.flow_attr.num_of_specs) + kfree(kern_flow_attr); + return err; +} + +ssize_t ib_uverbs_destroy_flow(struct ib_uverbs_file *file, + const char __user *buf, int in_len, + int out_len) { + struct ib_uverbs_destroy_flow cmd; + struct ib_flow *flow_id; + struct ib_uobject *uobj; + int ret; + + if (copy_from_user(&cmd, buf, sizeof(cmd))) + return -EFAULT; + + uobj = idr_write_uobj(&ib_uverbs_rule_idr, cmd.flow_handle, + file->ucontext); + if (!uobj) + return -EINVAL; + flow_id = uobj->object; + + ret = ib_destroy_flow(flow_id); + if (!ret) + uobj->live = 0; + + put_uobj_write(uobj); + + idr_remove_uobj(&ib_uverbs_rule_idr, uobj); + + mutex_lock(&file->mutex); + list_del(&uobj->list); + mutex_unlock(&file->mutex); + + put_uobj(uobj); + + return ret ? ret : in_len; +} + static int __uverbs_create_xsrq(struct ib_uverbs_file *file, struct ib_uverbs_create_xsrq *cmd, struct ib_udata *udata) diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index e4e7b2449d19..75ad86c4abf8 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -73,6 +73,7 @@ DEFINE_IDR(ib_uverbs_cq_idr); DEFINE_IDR(ib_uverbs_qp_idr); DEFINE_IDR(ib_uverbs_srq_idr); DEFINE_IDR(ib_uverbs_xrcd_idr); +DEFINE_IDR(ib_uverbs_rule_idr); static DEFINE_SPINLOCK(map_lock); static DECLARE_BITMAP(dev_map, IB_UVERBS_MAX_DEVICES); @@ -113,7 +114,9 @@ static ssize_t (*uverbs_cmd_table[])(struct ib_uverbs_file *file, [IB_USER_VERBS_CMD_OPEN_XRCD] = ib_uverbs_open_xrcd, [IB_USER_VERBS_CMD_CLOSE_XRCD] = ib_uverbs_close_xrcd, [IB_USER_VERBS_CMD_CREATE_XSRQ] = ib_uverbs_create_xsrq, - [IB_USER_VERBS_CMD_OPEN_QP] = ib_uverbs_open_qp + [IB_USER_VERBS_CMD_OPEN_QP] = ib_uverbs_open_qp, + [IB_USER_VERBS_CMD_CREATE_FLOW] = ib_uverbs_create_flow, + [IB_USER_VERBS_CMD_DESTROY_FLOW] = ib_uverbs_destroy_flow }; static void ib_uverbs_add_one(struct ib_device *device); @@ -212,6 +215,14 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, kfree(uobj); } + list_for_each_entry_safe(uobj, tmp, &context->rule_list, list) { + struct ib_flow *flow_id = uobj->object; + + idr_remove_uobj(&ib_uverbs_rule_idr, uobj); + ib_destroy_flow(flow_id); + kfree(uobj); + } + list_for_each_entry_safe(uobj, tmp, &context->qp_list, list) { struct ib_qp *qp = uobj->object; struct ib_uqp_object *uqp = diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 6f874b00491a..274205d4df97 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -954,6 +954,7 @@ struct ib_ucontext { struct list_head srq_list; struct list_head ah_list; struct list_head xrcd_list; + struct list_head rule_list; int closing; }; diff --git a/include/uapi/rdma/ib_user_verbs.h b/include/uapi/rdma/ib_user_verbs.h index 61535aa0a62e..0b233c56b0e4 100644 --- a/include/uapi/rdma/ib_user_verbs.h +++ b/include/uapi/rdma/ib_user_verbs.h @@ -86,7 +86,9 @@ enum { IB_USER_VERBS_CMD_OPEN_XRCD, IB_USER_VERBS_CMD_CLOSE_XRCD, IB_USER_VERBS_CMD_CREATE_XSRQ, - IB_USER_VERBS_CMD_OPEN_QP + IB_USER_VERBS_CMD_OPEN_QP, + IB_USER_VERBS_CMD_CREATE_FLOW = IB_USER_VERBS_CMD_THRESHOLD, + IB_USER_VERBS_CMD_DESTROY_FLOW }; /* @@ -694,6 +696,91 @@ struct ib_uverbs_detach_mcast { __u64 driver_data[0]; }; +struct ib_kern_eth_filter { + __u8 dst_mac[6]; + __u8 src_mac[6]; + __be16 ether_type; + __be16 vlan_tag; +}; + +struct ib_kern_spec_eth { + __u32 type; + __u16 size; + __u16 reserved; + struct ib_kern_eth_filter val; + struct ib_kern_eth_filter mask; +}; + +struct ib_kern_ipv4_filter { + __be32 src_ip; + __be32 dst_ip; +}; + +struct ib_kern_spec_ipv4 { + __u32 type; + __u16 size; + __u16 reserved; + struct ib_kern_ipv4_filter val; + struct ib_kern_ipv4_filter mask; +}; + +struct ib_kern_tcp_udp_filter { + __be16 dst_port; + __be16 src_port; +}; + +struct ib_kern_spec_tcp_udp { + __u32 type; + __u16 size; + __u16 reserved; + struct ib_kern_tcp_udp_filter val; + struct ib_kern_tcp_udp_filter mask; +}; + +struct ib_kern_spec { + union { + struct { + __u32 type; + __u16 size; + __u16 reserved; + }; + struct ib_kern_spec_eth eth; + struct ib_kern_spec_ipv4 ipv4; + struct ib_kern_spec_tcp_udp tcp_udp; + }; +}; + +struct ib_kern_flow_attr { + __u32 type; + __u16 size; + __u16 priority; + __u8 num_of_specs; + __u8 reserved[2]; + __u8 port; + __u32 flags; + /* Following are the optional layers according to user request + * struct ib_flow_spec_xxx + * struct ib_flow_spec_yyy + */ +}; + +struct ib_uverbs_create_flow { + __u32 comp_mask; + __u64 response; + __u32 qp_handle; + struct ib_kern_flow_attr flow_attr; +}; + +struct ib_uverbs_create_flow_resp { + __u32 comp_mask; + __u32 flow_handle; +}; + +struct ib_uverbs_destroy_flow { + __u32 comp_mask; + __u32 flow_handle; +}; + struct ib_uverbs_create_srq { __u64 response; __u64 user_handle; -- cgit v1.2.3-71-gd317 From f77c0162a339400ad16f657603fdc3bf11654fd3 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Wed, 14 Aug 2013 13:58:31 +0300 Subject: IB/mlx4: Add receive flow steering support Implement ib_create_flow() and ib_destroy_flow(). Translate the verbs structures provided by the user to HW structures and call the MLX4_QP_FLOW_STEERING_ATTACH/DETACH firmware commands. On the ATTACH command completion, the firmware provides a 64-bit registration ID, which is placed into struct mlx4_ib_flow that wraps the instance of struct ib_flow which is retuned to caller. Later, this reg ID is used for detaching that flow from the firmware. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: Roland Dreier --- drivers/infiniband/hw/mlx4/main.c | 235 +++++++++++++++++++++++++++++++++++ drivers/infiniband/hw/mlx4/mlx4_ib.h | 12 ++ include/linux/mlx4/device.h | 5 - 3 files changed, 247 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index a188d3178559..d6c5a73becf4 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -54,6 +54,8 @@ #define DRV_VERSION "1.0" #define DRV_RELDATE "April 4, 2008" +#define MLX4_IB_FLOW_MAX_PRIO 0xFFF + MODULE_AUTHOR("Roland Dreier"); MODULE_DESCRIPTION("Mellanox ConnectX HCA InfiniBand driver"); MODULE_LICENSE("Dual BSD/GPL"); @@ -88,6 +90,25 @@ static void init_query_mad(struct ib_smp *mad) static union ib_gid zgid; +static int check_flow_steering_support(struct mlx4_dev *dev) +{ + int ib_num_ports = 0; + int i; + + mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_IB) + ib_num_ports++; + + if (dev->caps.steering_mode == MLX4_STEERING_MODE_DEVICE_MANAGED) { + if (ib_num_ports || mlx4_is_mfunc(dev)) { + pr_warn("Device managed flow steering is unavailable " + "for IB ports or in multifunction env.\n"); + return 0; + } + return 1; + } + return 0; +} + static int mlx4_ib_query_device(struct ib_device *ibdev, struct ib_device_attr *props) { @@ -144,6 +165,8 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, props->device_cap_flags |= IB_DEVICE_MEM_WINDOW_TYPE_2B; else props->device_cap_flags |= IB_DEVICE_MEM_WINDOW_TYPE_2A; + if (check_flow_steering_support(dev->dev)) + props->device_cap_flags |= IB_DEVICE_MANAGED_FLOW_STEERING; } props->vendor_id = be32_to_cpup((__be32 *) (out_mad->data + 36)) & @@ -798,6 +821,209 @@ struct mlx4_ib_steering { union ib_gid gid; }; +static int parse_flow_attr(struct mlx4_dev *dev, + union ib_flow_spec *ib_spec, + struct _rule_hw *mlx4_spec) +{ + enum mlx4_net_trans_rule_id type; + + switch (ib_spec->type) { + case IB_FLOW_SPEC_ETH: + type = MLX4_NET_TRANS_RULE_ID_ETH; + memcpy(mlx4_spec->eth.dst_mac, ib_spec->eth.val.dst_mac, + ETH_ALEN); + memcpy(mlx4_spec->eth.dst_mac_msk, ib_spec->eth.mask.dst_mac, + ETH_ALEN); + mlx4_spec->eth.vlan_tag = ib_spec->eth.val.vlan_tag; + mlx4_spec->eth.vlan_tag_msk = ib_spec->eth.mask.vlan_tag; + break; + + case IB_FLOW_SPEC_IPV4: + type = MLX4_NET_TRANS_RULE_ID_IPV4; + mlx4_spec->ipv4.src_ip = ib_spec->ipv4.val.src_ip; + mlx4_spec->ipv4.src_ip_msk = ib_spec->ipv4.mask.src_ip; + mlx4_spec->ipv4.dst_ip = ib_spec->ipv4.val.dst_ip; + mlx4_spec->ipv4.dst_ip_msk = ib_spec->ipv4.mask.dst_ip; + break; + + case IB_FLOW_SPEC_TCP: + case IB_FLOW_SPEC_UDP: + type = ib_spec->type == IB_FLOW_SPEC_TCP ? + MLX4_NET_TRANS_RULE_ID_TCP : + MLX4_NET_TRANS_RULE_ID_UDP; + mlx4_spec->tcp_udp.dst_port = ib_spec->tcp_udp.val.dst_port; + mlx4_spec->tcp_udp.dst_port_msk = ib_spec->tcp_udp.mask.dst_port; + mlx4_spec->tcp_udp.src_port = ib_spec->tcp_udp.val.src_port; + mlx4_spec->tcp_udp.src_port_msk = ib_spec->tcp_udp.mask.src_port; + break; + + default: + return -EINVAL; + } + if (mlx4_map_sw_to_hw_steering_id(dev, type) < 0 || + mlx4_hw_rule_sz(dev, type) < 0) + return -EINVAL; + mlx4_spec->id = cpu_to_be16(mlx4_map_sw_to_hw_steering_id(dev, type)); + mlx4_spec->size = mlx4_hw_rule_sz(dev, type) >> 2; + return mlx4_hw_rule_sz(dev, type); +} + +static int __mlx4_ib_create_flow(struct ib_qp *qp, struct ib_flow_attr *flow_attr, + int domain, + enum mlx4_net_trans_promisc_mode flow_type, + u64 *reg_id) +{ + int ret, i; + int size = 0; + void *ib_flow; + struct mlx4_ib_dev *mdev = to_mdev(qp->device); + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_net_trans_rule_hw_ctrl *ctrl; + size_t rule_size = sizeof(struct mlx4_net_trans_rule_hw_ctrl) + + (sizeof(struct _rule_hw) * flow_attr->num_of_specs); + + static const u16 __mlx4_domain[] = { + [IB_FLOW_DOMAIN_USER] = MLX4_DOMAIN_UVERBS, + [IB_FLOW_DOMAIN_ETHTOOL] = MLX4_DOMAIN_ETHTOOL, + [IB_FLOW_DOMAIN_RFS] = MLX4_DOMAIN_RFS, + [IB_FLOW_DOMAIN_NIC] = MLX4_DOMAIN_NIC, + }; + + if (flow_attr->priority > MLX4_IB_FLOW_MAX_PRIO) { + pr_err("Invalid priority value %d\n", flow_attr->priority); + return -EINVAL; + } + + if (domain >= IB_FLOW_DOMAIN_NUM) { + pr_err("Invalid domain value %d\n", domain); + return -EINVAL; + } + + if (mlx4_map_sw_to_hw_steering_mode(mdev->dev, flow_type) < 0) + return -EINVAL; + + mailbox = mlx4_alloc_cmd_mailbox(mdev->dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + memset(mailbox->buf, 0, rule_size); + ctrl = mailbox->buf; + + ctrl->prio = cpu_to_be16(__mlx4_domain[domain] | + flow_attr->priority); + ctrl->type = mlx4_map_sw_to_hw_steering_mode(mdev->dev, flow_type); + ctrl->port = flow_attr->port; + ctrl->qpn = cpu_to_be32(qp->qp_num); + + ib_flow = flow_attr + 1; + size += sizeof(struct mlx4_net_trans_rule_hw_ctrl); + for (i = 0; i < flow_attr->num_of_specs; i++) { + ret = parse_flow_attr(mdev->dev, ib_flow, mailbox->buf + size); + if (ret < 0) { + mlx4_free_cmd_mailbox(mdev->dev, mailbox); + return -EINVAL; + } + ib_flow += ((union ib_flow_spec *) ib_flow)->size; + size += ret; + } + + ret = mlx4_cmd_imm(mdev->dev, mailbox->dma, reg_id, size >> 2, 0, + MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (ret == -ENOMEM) + pr_err("mcg table is full. Fail to register network rule.\n"); + else if (ret == -ENXIO) + pr_err("Device managed flow steering is disabled. Fail to register network rule.\n"); + else if (ret) + pr_err("Invalid argumant. Fail to register network rule.\n"); + + mlx4_free_cmd_mailbox(mdev->dev, mailbox); + return ret; +} + +static int __mlx4_ib_destroy_flow(struct mlx4_dev *dev, u64 reg_id) +{ + int err; + err = mlx4_cmd(dev, reg_id, 0, 0, + MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) + pr_err("Fail to detach network rule. registration id = 0x%llx\n", + reg_id); + return err; +} + +static struct ib_flow *mlx4_ib_create_flow(struct ib_qp *qp, + struct ib_flow_attr *flow_attr, + int domain) +{ + int err = 0, i = 0; + struct mlx4_ib_flow *mflow; + enum mlx4_net_trans_promisc_mode type[2]; + + memset(type, 0, sizeof(type)); + + mflow = kzalloc(sizeof(*mflow), GFP_KERNEL); + if (!mflow) { + err = -ENOMEM; + goto err_free; + } + + switch (flow_attr->type) { + case IB_FLOW_ATTR_NORMAL: + type[0] = MLX4_FS_REGULAR; + break; + + case IB_FLOW_ATTR_ALL_DEFAULT: + type[0] = MLX4_FS_ALL_DEFAULT; + break; + + case IB_FLOW_ATTR_MC_DEFAULT: + type[0] = MLX4_FS_MC_DEFAULT; + break; + + case IB_FLOW_ATTR_SNIFFER: + type[0] = MLX4_FS_UC_SNIFFER; + type[1] = MLX4_FS_MC_SNIFFER; + break; + + default: + err = -EINVAL; + goto err_free; + } + + while (i < ARRAY_SIZE(type) && type[i]) { + err = __mlx4_ib_create_flow(qp, flow_attr, domain, type[i], + &mflow->reg_id[i]); + if (err) + goto err_free; + i++; + } + + return &mflow->ibflow; + +err_free: + kfree(mflow); + return ERR_PTR(err); +} + +static int mlx4_ib_destroy_flow(struct ib_flow *flow_id) +{ + int err, ret = 0; + int i = 0; + struct mlx4_ib_dev *mdev = to_mdev(flow_id->qp->device); + struct mlx4_ib_flow *mflow = to_mflow(flow_id); + + while (i < ARRAY_SIZE(mflow->reg_id) && mflow->reg_id[i]) { + err = __mlx4_ib_destroy_flow(mdev->dev, mflow->reg_id[i]); + if (err) + ret = err; + i++; + } + + kfree(mflow); + return ret; +} + static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) { int err; @@ -1461,6 +1687,15 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) (1ull << IB_USER_VERBS_CMD_CLOSE_XRCD); } + if (check_flow_steering_support(dev)) { + ibdev->ib_dev.create_flow = mlx4_ib_create_flow; + ibdev->ib_dev.destroy_flow = mlx4_ib_destroy_flow; + + ibdev->ib_dev.uverbs_cmd_mask |= + (1ull << IB_USER_VERBS_CMD_CREATE_FLOW) | + (1ull << IB_USER_VERBS_CMD_DESTROY_FLOW); + } + mlx4_ib_alloc_eqs(dev, ibdev); spin_lock_init(&iboe->lock); diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index f61ec26500c4..036b663dd26e 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -132,6 +132,12 @@ struct mlx4_ib_fmr { struct mlx4_fmr mfmr; }; +struct mlx4_ib_flow { + struct ib_flow ibflow; + /* translating DMFS verbs sniffer rule to FW API requires two reg IDs */ + u64 reg_id[2]; +}; + struct mlx4_ib_wq { u64 *wrid; spinlock_t lock; @@ -552,6 +558,12 @@ static inline struct mlx4_ib_fmr *to_mfmr(struct ib_fmr *ibfmr) { return container_of(ibfmr, struct mlx4_ib_fmr, ibfmr); } + +static inline struct mlx4_ib_flow *to_mflow(struct ib_flow *ibflow) +{ + return container_of(ibflow, struct mlx4_ib_flow, ibflow); +} + static inline struct mlx4_ib_qp *to_mqp(struct ib_qp *ibqp) { return container_of(ibqp, struct mlx4_ib_qp, ibqp); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 52c23a892bab..d73423c37c25 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -1052,11 +1052,6 @@ struct _rule_hw { }; }; -/* translating DMFS verbs sniffer rule to the FW API would need two reg IDs */ -struct mlx4_flow_handle { - u64 reg_id[2]; -}; - int mlx4_flow_steer_promisc_add(struct mlx4_dev *dev, u8 port, u32 qpn, enum mlx4_net_trans_promisc_mode mode); int mlx4_flow_steer_promisc_remove(struct mlx4_dev *dev, u8 port, -- cgit v1.2.3-71-gd317 From fbf501c347b2eea8451a615bd823b6b91a1a8eed Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 27 Aug 2013 10:28:25 -0600 Subject: PCI: Clarify PCI_EXP_TYPE_PCI_BRIDGE comment The PCI_EXP_TYPE_PCI_BRIDGE is a *PCIe* function that is a bridge to PCI/PCI-X. See PCIe spec r3.0, sec 7.8.2. Signed-off-by: Bjorn Helgaas --- include/uapi/linux/pci_regs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 4b8f2e3bad58..5bb33f485271 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -421,8 +421,8 @@ #define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ #define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ #define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ -#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ -#define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIE Bridge */ +#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCIe to PCI/PCI-X Bridge */ +#define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 /* PCI/PCI-X to PCIe Bridge */ #define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */ #define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */ #define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ -- cgit v1.2.3-71-gd317 From 1b121c24dd4a9bb7156f10c7da3f39515a9fa6f0 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 27 Aug 2013 11:10:02 -0600 Subject: PCI: Remove obsolete comment reference to pci_pcie_cap2() pci_pcie_cap2() was replaced by pcie_capability_read_word() and similar functions, so update the comment. Signed-off-by: Bjorn Helgaas --- include/uapi/linux/pci_regs.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 5bb33f485271..591bb3b2ee4a 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -544,10 +544,12 @@ #define PCI_EXP_RTSTA_PME 0x10000 /* PME status */ #define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */ /* - * Note that the following PCI Express 'Capability Structure' registers - * were introduced with 'Capability Version' 0x2 (v2). These registers - * do not exist on devices with Capability Version 1. Use pci_pcie_cap2() - * to use these fields safely. + * The Device Capabilities 2, Device Status 2, Device Control 2, + * Link Capabilities 2, Link Status 2, Link Control 2, + * Slot Capabilities 2, Slot Status 2, and Slot Control 2 registers + * are only present on devices with PCIe Capability version 2. + * Use pcie_capability_read_word() and similar interfaces to use them + * safely. */ #define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ #define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ -- cgit v1.2.3-71-gd317 From c0b4b3815d4e65c082d6e85d0fccf25b230e7890 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 27 Aug 2013 11:28:36 -0600 Subject: PCI: Tidy bitmasks and spacing of PCIe capability definitions The convention of showing bits in a mask of the full register width, e.g., "0x00000007" instead of "0x07" for a field in a 32-bit register, is common but not universal in this file. This patch makes it consistently used at least for the PCIe capability. Whitespace and zero-extension changes only; no functional change. Signed-off-by: Bjorn Helgaas --- include/uapi/linux/pci_regs.h | 90 +++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 591bb3b2ee4a..28c83eceefe3 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -428,17 +428,17 @@ #define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ #define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ #define PCI_EXP_DEVCAP 4 /* Device capabilities */ -#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ -#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */ -#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */ -#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ -#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ -#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ -#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ -#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ -#define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ -#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ -#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ +#define PCI_EXP_DEVCAP_PAYLOAD 0x00000007 /* Max_Payload_Size */ +#define PCI_EXP_DEVCAP_PHANTOM 0x00000018 /* Phantom functions */ +#define PCI_EXP_DEVCAP_EXT_TAG 0x00000020 /* Extended tags */ +#define PCI_EXP_DEVCAP_L0S 0x000001c0 /* L0s Acceptable Latency */ +#define PCI_EXP_DEVCAP_L1 0x00000e00 /* L1 Acceptable Latency */ +#define PCI_EXP_DEVCAP_ATN_BUT 0x00001000 /* Attention Button Present */ +#define PCI_EXP_DEVCAP_ATN_IND 0x00002000 /* Attention Indicator Present */ +#define PCI_EXP_DEVCAP_PWR_IND 0x00004000 /* Power Indicator Present */ +#define PCI_EXP_DEVCAP_RBER 0x00008000 /* Role-Based Error Reporting */ +#define PCI_EXP_DEVCAP_PWR_VAL 0x03fc0000 /* Slot Power Limit Value */ +#define PCI_EXP_DEVCAP_PWR_SCL 0x0c000000 /* Slot Power Limit Scale */ #define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ #define PCI_EXP_DEVCTL 8 /* Device Control */ #define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ @@ -454,16 +454,16 @@ #define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ #define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ #define PCI_EXP_DEVSTA 10 /* Device Status */ -#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ -#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ -#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */ -#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */ -#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ -#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ +#define PCI_EXP_DEVSTA_CED 0x0001 /* Correctable Error Detected */ +#define PCI_EXP_DEVSTA_NFED 0x0002 /* Non-Fatal Error Detected */ +#define PCI_EXP_DEVSTA_FED 0x0004 /* Fatal Error Detected */ +#define PCI_EXP_DEVSTA_URD 0x0008 /* Unsupported Request Detected */ +#define PCI_EXP_DEVSTA_AUXPD 0x0010 /* AUX Power Detected */ +#define PCI_EXP_DEVSTA_TRPND 0x0020 /* Transactions Pending */ #define PCI_EXP_LNKCAP 12 /* Link Capabilities */ #define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */ -#define PCI_EXP_LNKCAP_SLS_2_5GB 0x1 /* LNKCAP2 SLS Vector bit 0 (2.5GT/s) */ -#define PCI_EXP_LNKCAP_SLS_5_0GB 0x2 /* LNKCAP2 SLS Vector bit 1 (5.0GT/s) */ +#define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */ +#define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ #define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ #define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ #define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ @@ -475,21 +475,21 @@ #define PCI_EXP_LNKCAP_PN 0xff000000 /* Port Number */ #define PCI_EXP_LNKCTL 16 /* Link Control */ #define PCI_EXP_LNKCTL_ASPMC 0x0003 /* ASPM Control */ -#define PCI_EXP_LNKCTL_ASPM_L0S 0x01 /* L0s Enable */ -#define PCI_EXP_LNKCTL_ASPM_L1 0x02 /* L1 Enable */ +#define PCI_EXP_LNKCTL_ASPM_L0S 0x0001 /* L0s Enable */ +#define PCI_EXP_LNKCTL_ASPM_L1 0x0002 /* L1 Enable */ #define PCI_EXP_LNKCTL_RCB 0x0008 /* Read Completion Boundary */ #define PCI_EXP_LNKCTL_LD 0x0010 /* Link Disable */ #define PCI_EXP_LNKCTL_RL 0x0020 /* Retrain Link */ #define PCI_EXP_LNKCTL_CCC 0x0040 /* Common Clock Configuration */ #define PCI_EXP_LNKCTL_ES 0x0080 /* Extended Synch */ -#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ +#define PCI_EXP_LNKCTL_CLKREQ_EN 0x0100 /* Enable clkreq */ #define PCI_EXP_LNKCTL_HAWD 0x0200 /* Hardware Autonomous Width Disable */ #define PCI_EXP_LNKCTL_LBMIE 0x0400 /* Link Bandwidth Management Interrupt Enable */ #define PCI_EXP_LNKCTL_LABIE 0x0800 /* Lnk Autonomous Bandwidth Interrupt Enable */ #define PCI_EXP_LNKSTA 18 /* Link Status */ #define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */ -#define PCI_EXP_LNKSTA_CLS_2_5GB 0x01 /* Current Link Speed 2.5GT/s */ -#define PCI_EXP_LNKSTA_CLS_5_0GB 0x02 /* Current Link Speed 5.0GT/s */ +#define PCI_EXP_LNKSTA_CLS_2_5GB 0x0001 /* Current Link Speed 2.5GT/s */ +#define PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 /* Current Link Speed 5.0GT/s */ #define PCI_EXP_LNKSTA_NLW 0x03f0 /* Nogotiated Link Width */ #define PCI_EXP_LNKSTA_NLW_SHIFT 4 /* start of NLW mask in link status */ #define PCI_EXP_LNKSTA_LT 0x0800 /* Link Training */ @@ -534,15 +534,15 @@ #define PCI_EXP_SLTSTA_EIS 0x0080 /* Electromechanical Interlock Status */ #define PCI_EXP_SLTSTA_DLLSC 0x0100 /* Data Link Layer State Changed */ #define PCI_EXP_RTCTL 28 /* Root Control */ -#define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ -#define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ -#define PCI_EXP_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */ -#define PCI_EXP_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */ -#define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ +#define PCI_EXP_RTCTL_SECEE 0x0001 /* System Error on Correctable Error */ +#define PCI_EXP_RTCTL_SENFEE 0x0002 /* System Error on Non-Fatal Error */ +#define PCI_EXP_RTCTL_SEFEE 0x0004 /* System Error on Fatal Error */ +#define PCI_EXP_RTCTL_PMEIE 0x0008 /* PME Interrupt Enable */ +#define PCI_EXP_RTCTL_CRSSVE 0x0010 /* CRS Software Visibility Enable */ #define PCI_EXP_RTCAP 30 /* Root Capabilities */ #define PCI_EXP_RTSTA 32 /* Root Status */ -#define PCI_EXP_RTSTA_PME 0x10000 /* PME status */ -#define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */ +#define PCI_EXP_RTSTA_PME 0x00010000 /* PME status */ +#define PCI_EXP_RTSTA_PENDING 0x00020000 /* PME pending */ /* * The Device Capabilities 2, Device Status 2, Device Control 2, * Link Capabilities 2, Link Status 2, Link Control 2, @@ -552,25 +552,25 @@ * safely. */ #define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ -#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ -#define PCI_EXP_DEVCAP2_LTR 0x800 /* Latency tolerance reporting */ -#define PCI_EXP_DEVCAP2_OBFF_MASK 0xc0000 /* OBFF support mechanism */ -#define PCI_EXP_DEVCAP2_OBFF_MSG 0x40000 /* New message signaling */ -#define PCI_EXP_DEVCAP2_OBFF_WAKE 0x80000 /* Re-use WAKE# for OBFF */ +#define PCI_EXP_DEVCAP2_ARI 0x00000020 /* Alternative Routing-ID */ +#define PCI_EXP_DEVCAP2_LTR 0x00000800 /* Latency tolerance reporting */ +#define PCI_EXP_DEVCAP2_OBFF_MASK 0x000c0000 /* OBFF support mechanism */ +#define PCI_EXP_DEVCAP2_OBFF_MSG 0x00040000 /* New message signaling */ +#define PCI_EXP_DEVCAP2_OBFF_WAKE 0x00080000 /* Re-use WAKE# for OBFF */ #define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ #define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ -#define PCI_EXP_DEVCTL2_IDO_REQ_EN 0x100 /* ID-based ordering request enable */ -#define PCI_EXP_DEVCTL2_IDO_CMP_EN 0x200 /* ID-based ordering completion enable */ -#define PCI_EXP_DEVCTL2_LTR_EN 0x400 /* Latency tolerance reporting */ -#define PCI_EXP_DEVCTL2_OBFF_MSGA_EN 0x2000 /* OBFF enable with Message type A */ -#define PCI_EXP_DEVCTL2_OBFF_MSGB_EN 0x4000 /* OBFF enable with Message type B */ +#define PCI_EXP_DEVCTL2_IDO_REQ_EN 0x0100 /* Allow IDO for requests */ +#define PCI_EXP_DEVCTL2_IDO_CMP_EN 0x0200 /* Allow IDO for completions */ +#define PCI_EXP_DEVCTL2_LTR_EN 0x0400 /* Enable LTR mechanism */ +#define PCI_EXP_DEVCTL2_OBFF_MSGA_EN 0x2000 /* Enable OBFF Message type A */ +#define PCI_EXP_DEVCTL2_OBFF_MSGB_EN 0x4000 /* Enable OBFF Message type B */ #define PCI_EXP_DEVCTL2_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 44 /* v2 endpoints end here */ #define PCI_EXP_LNKCAP2 44 /* Link Capability 2 */ -#define PCI_EXP_LNKCAP2_SLS_2_5GB 0x02 /* Supported Link Speed 2.5GT/s */ -#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x04 /* Supported Link Speed 5.0GT/s */ -#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x08 /* Supported Link Speed 8.0GT/s */ -#define PCI_EXP_LNKCAP2_CROSSLINK 0x100 /* Crosslink supported */ +#define PCI_EXP_LNKCAP2_SLS_2_5GB 0x00000002 /* Supported Speed 2.5GT/s */ +#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5.0GT/s */ +#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8.0GT/s */ +#define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ #define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ -- cgit v1.2.3-71-gd317 From bd6fb762b595fb83758ae50d3610223244234a2d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 27 Aug 2013 12:17:59 -0600 Subject: PCI: Add offsets of PCIe capability registers These offsets are not used, and in some cases are completely reserved even in the spec, but I'm adding them for completeness just to match the diagrams in the spec, e.g., PCIe spec r3.0, sec 7.8. Signed-off-by: Bjorn Helgaas --- include/uapi/linux/pci_regs.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 28c83eceefe3..baa7852468ef 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -565,15 +565,18 @@ #define PCI_EXP_DEVCTL2_OBFF_MSGA_EN 0x2000 /* Enable OBFF Message type A */ #define PCI_EXP_DEVCTL2_OBFF_MSGB_EN 0x4000 /* Enable OBFF Message type B */ #define PCI_EXP_DEVCTL2_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ +#define PCI_EXP_DEVSTA2 42 /* Device Status 2 */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 44 /* v2 endpoints end here */ -#define PCI_EXP_LNKCAP2 44 /* Link Capability 2 */ +#define PCI_EXP_LNKCAP2 44 /* Link Capabilities 2 */ #define PCI_EXP_LNKCAP2_SLS_2_5GB 0x00000002 /* Supported Speed 2.5GT/s */ #define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5.0GT/s */ #define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8.0GT/s */ #define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ +#define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */ #define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ +#define PCI_EXP_SLTSTA2 58 /* Slot Status 2 */ /* Extended Capabilities (PCI-X 2.0 and Express) */ #define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) -- cgit v1.2.3-71-gd317 From 8ef2d6511f7eba89ef5fe41cc83008ae63368aa2 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 27 Aug 2013 17:47:34 +0300 Subject: dev-core: fix build break when DEBUG is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When DEBUG is defined, dev_dbg_ratelimited uses dynamic debug data structures even when CONFIG_DYNAMIC_DEBUG is not defined. It leads to build break. For example, when I try to use dev_dbg_ratelimited in USB code and CONFIG_USB_DEBUG is enabled, but CONFIG_DYNAMIC_DEBUG is not, I get: CC [M] drivers/usb/host/xhci-ring.o drivers/usb/host/xhci-ring.c: In function ‘xhci_queue_intr_tx’: drivers/usb/host/xhci-ring.c:3059:3: error: implicit declaration of function ‘DEFINE_DYNAMIC_DEBUG_METADATA’ [-Werror=implicit-function-declaration] drivers/usb/host/xhci-ring.c:3059:3: error: ‘descriptor’ undeclared (first use in this function) drivers/usb/host/xhci-ring.c:3059:3: note: each undeclared identifier is reported only once for each function it appears in drivers/usb/host/xhci-ring.c:3059:3: error: implicit declaration of function ‘__dynamic_pr_debug’ [-Werror=implicit-function-declaration] drivers/usb/host/xhci-ring.c: In function ‘xhci_queue_isoc_tx_prepare’: drivers/usb/host/xhci-ring.c:3847:3: error: ‘descriptor’ undeclared (first use in this function) cc1: some warnings being treated as errors make[2]: *** [drivers/usb/host/xhci-ring.o] Error 1 make[1]: *** [drivers/usb/host] Error 2 make: *** [drivers/usb/] Error 2 This patch separates definition for CONFIG_DYNAMIC_DEBUG and DEBUG cases. [Note, Sarah moved the comment above the macro to avoid checkpatch warnings.] Signed-off-by: Dmitry Kasatkin Signed-off-by: Sarah Sharp Acked-by: Greg Kroah-Hartman --- include/linux/device.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/device.h b/include/linux/device.h index 22b546a58591..7d960d581e9c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1099,7 +1099,8 @@ do { \ dev_level_ratelimited(dev_notice, dev, fmt, ##__VA_ARGS__) #define dev_info_ratelimited(dev, fmt, ...) \ dev_level_ratelimited(dev_info, dev, fmt, ##__VA_ARGS__) -#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) +#if defined(CONFIG_DYNAMIC_DEBUG) +/* descriptor check is first to prevent flooding with "callbacks suppressed" */ #define dev_dbg_ratelimited(dev, fmt, ...) \ do { \ static DEFINE_RATELIMIT_STATE(_rs, \ @@ -1108,8 +1109,17 @@ do { \ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \ if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT) && \ __ratelimit(&_rs)) \ - __dynamic_pr_debug(&descriptor, pr_fmt(fmt), \ - ##__VA_ARGS__); \ + __dynamic_dev_dbg(&descriptor, dev, fmt, \ + ##__VA_ARGS__); \ +} while (0) +#elif defined(DEBUG) +#define dev_dbg_ratelimited(dev, fmt, ...) \ +do { \ + static DEFINE_RATELIMIT_STATE(_rs, \ + DEFAULT_RATELIMIT_INTERVAL, \ + DEFAULT_RATELIMIT_BURST); \ + if (__ratelimit(&_rs)) \ + dev_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__); \ } while (0) #else #define dev_dbg_ratelimited(dev, fmt, ...) \ -- cgit v1.2.3-71-gd317 From 10d9eb8a17cfb697967928bde06f3e7e530b03ac Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 19 Mar 2013 23:59:04 -0400 Subject: drivers: irq-chip: irq-gic: introduce gic_cpu_if_down() When processors are about to hit low power states, the assertion of standbywfi signal, triggered by the wfi instruction, is essential to entering low power modes. If an IRQ is pending on the processor at the time wfi is issued, the wfi instruction completes and the processor restarts execution without asserting the standbywfi signal. Depending on the platform power controller HW this behaviour can be acceptable or not; if this behaviour must be prevented software should be provided with a way to disable the routing of interrupts to the core IRQ pins. On systems where raw GIC distributor interrupts are connected to the power controller as wake-up events (hence the power controller still senses IRQs and can wake up cores upon IRQ pending), the GIC CPU interface can be disabled on power down, so that the GIC CPU IF output is gated and wfi cannot complete, thereby preventing the standbywfi issue. This patch adds a simple function to the GIC driver that allows to disable the GIC CPU IF from power down procedures. Signed-off-by: Nicolas Pitre Signed-off-by: Lorenzo Pieralisi [rewrote commit log] Signed-off-by: Olof Johansson --- drivers/irqchip/irq-gic.c | 6 ++++++ include/linux/irqchip/arm-gic.h | 1 + 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index ee7c50312066..d0e948084eaf 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -453,6 +453,12 @@ static void gic_cpu_init(struct gic_chip_data *gic) writel_relaxed(1, base + GIC_CPU_CTRL); } +void gic_cpu_if_down(void) +{ + void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]); + writel_relaxed(0, cpu_base + GIC_CPU_CTRL); +} + #ifdef CONFIG_CPU_PM /* * Saves the GIC distributor registers during suspend or idle. Must be called diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 3e203eb23cc7..0e5d9ecdb2b6 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -66,6 +66,7 @@ extern struct irq_chip gic_arch_extn; void gic_init_bases(unsigned int, int, void __iomem *, void __iomem *, u32 offset, struct device_node *); void gic_cascade_irq(unsigned int gic_nr, unsigned int irq); +void gic_cpu_if_down(void); static inline void gic_init(unsigned int nr, int start, void __iomem *dist , void __iomem *cpu) -- cgit v1.2.3-71-gd317 From d7b2a00c2e2eedf460ce2a15237f28de40412d86 Mon Sep 17 00:00:00 2001 From: Zheng Liu Date: Wed, 28 Aug 2013 14:47:06 -0400 Subject: ext4: isolate ext4_extents.h file After applied the commit (4a092d73), we have reduced the number of source files that need to #include ext4_extents.h. But we can do better. This commit defines ext4_zeroout_es() in extents.c and move EXT_MAX_BLOCKS into ext4.h in order not to include ext4_extents.h in indirect.c and ioctl.c. Meanwhile we just need to include this file in extent_status.c when ES_AGGRESSIVE_TEST is defined. Otherwise, this commit removes a duplicated declaration in trace/events/ext4.h. After applied this patch, we just need to include ext4_extents.h file in {super,migrate,move_extents,extents}.c, and it is easy for us to define a new extent disk layout. Signed-off-by: Zheng Liu Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 6 ++++++ fs/ext4/ext4_extents.h | 6 ------ fs/ext4/extents.c | 21 +++++++++++++++++++-- fs/ext4/extents_status.c | 20 ++------------------ fs/ext4/extents_status.h | 1 - fs/ext4/indirect.c | 1 - fs/ext4/ioctl.c | 1 - include/trace/events/ext4.h | 1 - 8 files changed, 27 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 3dbc56eb4849..28896655ad41 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2686,6 +2686,12 @@ extern int ext4_check_blockref(const char *, unsigned int, struct ext4_ext_path; struct ext4_extent; +/* + * Maximum number of logical blocks in a file; ext4_extent's ee_block is + * __le32. + */ +#define EXT_MAX_BLOCKS 0xffffffff + extern int ext4_ext_tree_init(handle_t *handle, struct inode *); extern int ext4_ext_writepage_trans_blocks(struct inode *, int); extern int ext4_ext_index_trans_blocks(struct inode *inode, int extents); diff --git a/fs/ext4/ext4_extents.h b/fs/ext4/ext4_extents.h index 51bc821ade90..5074fe23f19e 100644 --- a/fs/ext4/ext4_extents.h +++ b/fs/ext4/ext4_extents.h @@ -133,12 +133,6 @@ struct ext4_ext_path { * structure for external API */ -/* - * Maximum number of logical blocks in a file; ext4_extent's ee_block is - * __le32. - */ -#define EXT_MAX_BLOCKS 0xffffffff - /* * EXT_INIT_MAX_LEN is the maximum number of blocks we can have in an * initialized extent. This is 2^15 and not (2^16 - 1), since we use the diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 916e88498c87..54d52afcdb19 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -3048,6 +3048,23 @@ void ext4_ext_release(struct super_block *sb) #endif } +static int ext4_zeroout_es(struct inode *inode, struct ext4_extent *ex) +{ + ext4_lblk_t ee_block; + ext4_fsblk_t ee_pblock; + unsigned int ee_len; + + ee_block = le32_to_cpu(ex->ee_block); + ee_len = ext4_ext_get_actual_len(ex); + ee_pblock = ext4_ext_pblock(ex); + + if (ee_len == 0) + return 0; + + return ext4_es_insert_extent(inode, ee_block, ee_len, ee_pblock, + EXTENT_STATUS_WRITTEN); +} + /* FIXME!! we need to try to merge to left or right after zero-out */ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex) { @@ -3200,7 +3217,7 @@ static int ext4_split_extent_at(handle_t *handle, goto fix_extent_len; /* update extent status tree */ - err = ext4_es_zeroout(inode, &zero_ex); + err = ext4_zeroout_es(inode, &zero_ex); goto out; } else if (err) @@ -3551,7 +3568,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle, out: /* If we have gotten a failure, don't zero out status tree */ if (!err) - err = ext4_es_zeroout(inode, &zero_ex); + err = ext4_zeroout_es(inode, &zero_ex); return err ? err : allocated; } diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index 0e88a367b535..2d1bdbe78c04 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c @@ -13,7 +13,6 @@ #include #include "ext4.h" #include "extents_status.h" -#include "ext4_extents.h" #include @@ -409,6 +408,8 @@ ext4_es_try_to_merge_right(struct inode *inode, struct extent_status *es) } #ifdef ES_AGGRESSIVE_TEST +#include "ext4_extents.h" /* Needed when ES_AGGRESSIVE_TEST is defined */ + static void ext4_es_insert_extent_ext_check(struct inode *inode, struct extent_status *es) { @@ -903,23 +904,6 @@ int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, return err; } -int ext4_es_zeroout(struct inode *inode, struct ext4_extent *ex) -{ - ext4_lblk_t ee_block; - ext4_fsblk_t ee_pblock; - unsigned int ee_len; - - ee_block = le32_to_cpu(ex->ee_block); - ee_len = ext4_ext_get_actual_len(ex); - ee_pblock = ext4_ext_pblock(ex); - - if (ee_len == 0) - return 0; - - return ext4_es_insert_extent(inode, ee_block, ee_len, ee_pblock, - EXTENT_STATUS_WRITTEN); -} - static int ext4_inode_touch_time_cmp(void *priv, struct list_head *a, struct list_head *b) { diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h index 3e83aef3653a..167f4ab8ecc3 100644 --- a/fs/ext4/extents_status.h +++ b/fs/ext4/extents_status.h @@ -81,7 +81,6 @@ extern void ext4_es_find_delayed_extent_range(struct inode *inode, struct extent_status *es); extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk, struct extent_status *es); -extern int ext4_es_zeroout(struct inode *inode, struct ext4_extent *ex); static inline int ext4_es_is_written(struct extent_status *es) { diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index 87b30cd357e7..594009f5f523 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -23,7 +23,6 @@ #include #include "ext4_jbd2.h" #include "truncate.h" -#include "ext4_extents.h" /* Needed for EXT_MAX_BLOCKS */ #include diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 5498f75a1648..a569d335f804 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -17,7 +17,6 @@ #include #include "ext4_jbd2.h" #include "ext4.h" -#include "ext4_extents.h" #define MAX_32_NUM ((((unsigned long long) 1) << 32) - 1) diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index d892b55d91ab..197d3125df2a 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -14,7 +14,6 @@ struct ext4_prealloc_space; struct ext4_inode_info; struct mpage_da_data; struct ext4_map_blocks; -struct ext4_extent; struct extent_status; #define EXT4_I(inode) (container_of(inode, struct ext4_inode_info, vfs_inode)) -- cgit v1.2.3-71-gd317 From 7e0bdf15cee7d2c809558b8169dc5b08792d0c82 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 18 Aug 2013 13:01:30 +0200 Subject: of: Make of_get_phy_mode() return int i.s.o. const int include/linux/of_net.h:16: warning: type qualifiers ignored on function return type Signed-off-by: Geert Uytterhoeven Signed-off-by: Grant Likely --- drivers/of/of_net.c | 2 +- include/linux/of_net.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/of/of_net.c b/drivers/of/of_net.c index ea174c8ee34b..8f9be2e09937 100644 --- a/drivers/of/of_net.c +++ b/drivers/of/of_net.c @@ -39,7 +39,7 @@ static const char *phy_modes[] = { * The function gets phy interface string from property 'phy-mode', * and return its index in phy_modes table, or errno in error case. */ -const int of_get_phy_mode(struct device_node *np) +int of_get_phy_mode(struct device_node *np) { const char *pm; int err, i; diff --git a/include/linux/of_net.h b/include/linux/of_net.h index 61bf53b02779..34597c8c1a4c 100644 --- a/include/linux/of_net.h +++ b/include/linux/of_net.h @@ -9,10 +9,10 @@ #ifdef CONFIG_OF_NET #include -extern const int of_get_phy_mode(struct device_node *np); +extern int of_get_phy_mode(struct device_node *np); extern const void *of_get_mac_address(struct device_node *np); #else -static inline const int of_get_phy_mode(struct device_node *np) +static inline int of_get_phy_mode(struct device_node *np) { return -ENODEV; } -- cgit v1.2.3-71-gd317 From 0d69704ae348c03bc216b01e32a0e9a2372be419 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 10 Sep 2012 12:28:36 +1000 Subject: gpu/vga_switcheroo: add driver control power feature. (v3) For optimus and powerxpress muxless we really want the GPU driver deciding when to power up/down the GPU, not userspace. This adds the ability for a driver to dynamically power up/down the GPU and remove the switcheroo from controlling it, the switcheroo reports the dynamic state to userspace also. It also adds 2 power domains, one for machine where the power switch is controlled outside the GPU D3 state, so the powerdown ordering is done correctly, and the second for the hdmi audio device to make sure it can resume for PCI config space accesses. v1.1: fix build with switcheroo off v2: add power domain support for radeon and v1 nvidia dsms v2.1: fix typo in off case v3: add audio power domain for hdmi audio + misc audio fixes v4: use PCI_SLOT macro, drop power reference on hdmi audio resume failure also. Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_dma.c | 2 +- drivers/gpu/drm/nouveau/nouveau_vga.c | 2 +- drivers/gpu/drm/radeon/radeon_device.c | 2 +- drivers/gpu/vga/vga_switcheroo.c | 147 +++++++++++++++++++++++++++++++-- include/linux/vga_switcheroo.h | 13 ++- 5 files changed, 156 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 0adfe4000871..54f86242e80e 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1293,7 +1293,7 @@ static int i915_load_modeset_init(struct drm_device *dev) intel_register_dsm_handler(); - ret = vga_switcheroo_register_client(dev->pdev, &i915_switcheroo_ops); + ret = vga_switcheroo_register_client(dev->pdev, &i915_switcheroo_ops, false); if (ret) goto cleanup_vga_client; diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c index 25d3495725eb..40a09f11a600 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vga.c +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c @@ -79,7 +79,7 @@ nouveau_vga_init(struct nouveau_drm *drm) { struct drm_device *dev = drm->dev; vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); - vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops); + vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, false); } void diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 82335e38ec4f..0610ca4fb6a3 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1269,7 +1269,7 @@ int radeon_device_init(struct radeon_device *rdev, /* this will fail for cards that aren't VGA class devices, just * ignore it */ vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); - vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops); + vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops, false); r = radeon_init(rdev); if (r) diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index cf787e1d9322..ec0ae2d1686a 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -37,6 +38,7 @@ struct vga_switcheroo_client { const struct vga_switcheroo_client_ops *ops; int id; bool active; + bool driver_power_control; struct list_head list; }; @@ -132,7 +134,7 @@ EXPORT_SYMBOL(vga_switcheroo_unregister_handler); static int register_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, - int id, bool active) + int id, bool active, bool driver_power_control) { struct vga_switcheroo_client *client; @@ -145,6 +147,7 @@ static int register_client(struct pci_dev *pdev, client->ops = ops; client->id = id; client->active = active; + client->driver_power_control = driver_power_control; mutex_lock(&vgasr_mutex); list_add_tail(&client->list, &vgasr_priv.clients); @@ -160,10 +163,11 @@ static int register_client(struct pci_dev *pdev, } int vga_switcheroo_register_client(struct pci_dev *pdev, - const struct vga_switcheroo_client_ops *ops) + const struct vga_switcheroo_client_ops *ops, + bool driver_power_control) { return register_client(pdev, ops, -1, - pdev == vga_default_device()); + pdev == vga_default_device(), driver_power_control); } EXPORT_SYMBOL(vga_switcheroo_register_client); @@ -171,7 +175,7 @@ int vga_switcheroo_register_audio_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, int id, bool active) { - return register_client(pdev, ops, id | ID_BIT_AUDIO, active); + return register_client(pdev, ops, id | ID_BIT_AUDIO, active, false); } EXPORT_SYMBOL(vga_switcheroo_register_audio_client); @@ -258,10 +262,11 @@ static int vga_switcheroo_show(struct seq_file *m, void *v) int i = 0; mutex_lock(&vgasr_mutex); list_for_each_entry(client, &vgasr_priv.clients, list) { - seq_printf(m, "%d:%s%s:%c:%s:%s\n", i, + seq_printf(m, "%d:%s%s:%c:%s%s:%s\n", i, client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", client_is_vga(client) ? "" : "-Audio", client->active ? '+' : ' ', + client->driver_power_control ? "Dyn" : "", client->pwr_state ? "Pwr" : "Off", pci_name(client->pdev)); i++; @@ -277,6 +282,8 @@ static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file) static int vga_switchon(struct vga_switcheroo_client *client) { + if (client->driver_power_control) + return 0; if (vgasr_priv.handler->power_state) vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); /* call the driver callback to turn on device */ @@ -287,6 +294,8 @@ static int vga_switchon(struct vga_switcheroo_client *client) static int vga_switchoff(struct vga_switcheroo_client *client) { + if (client->driver_power_control) + return 0; /* call the driver callback to turn off device */ client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); if (vgasr_priv.handler->power_state) @@ -402,6 +411,8 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, list_for_each_entry(client, &vgasr_priv.clients, list) { if (client->active || client_is_audio(client)) continue; + if (client->driver_power_control) + continue; set_audio_state(client->id, VGA_SWITCHEROO_OFF); if (client->pwr_state == VGA_SWITCHEROO_ON) vga_switchoff(client); @@ -413,6 +424,8 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, list_for_each_entry(client, &vgasr_priv.clients, list) { if (client->active || client_is_audio(client)) continue; + if (client->driver_power_control) + continue; if (client->pwr_state == VGA_SWITCHEROO_OFF) vga_switchon(client); set_audio_state(client->id, VGA_SWITCHEROO_ON); @@ -565,3 +578,127 @@ err: return err; } EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); + +static void vga_switcheroo_power_switch(struct pci_dev *pdev, enum vga_switcheroo_state state) +{ + struct vga_switcheroo_client *client; + + if (!vgasr_priv.handler->power_state) + return; + + client = find_client_from_pci(&vgasr_priv.clients, pdev); + if (!client) + return; + + if (!client->driver_power_control) + return; + + vgasr_priv.handler->power_state(client->id, state); +} + +/* force a PCI device to a certain state - mainly to turn off audio clients */ + +void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic) +{ + struct vga_switcheroo_client *client; + + client = find_client_from_pci(&vgasr_priv.clients, pdev); + if (!client) + return; + + if (!client->driver_power_control) + return; + + client->pwr_state = dynamic; + set_audio_state(client->id, dynamic); +} +EXPORT_SYMBOL(vga_switcheroo_set_dynamic_switch); + +/* switcheroo power domain */ +static int vga_switcheroo_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + int ret; + + ret = dev->bus->pm->runtime_suspend(dev); + if (ret) + return ret; + + vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_OFF); + return 0; +} + +static int vga_switcheroo_runtime_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + int ret; + + vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_ON); + ret = dev->bus->pm->runtime_resume(dev); + if (ret) + return ret; + + return 0; +} + +/* this version is for the case where the power switch is separate + to the device being powered down. */ +int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain) +{ + /* copy over all the bus versions */ + if (dev->bus && dev->bus->pm) { + domain->ops = *dev->bus->pm; + domain->ops.runtime_suspend = vga_switcheroo_runtime_suspend; + domain->ops.runtime_resume = vga_switcheroo_runtime_resume; + + dev->pm_domain = domain; + return 0; + } + dev->pm_domain = NULL; + return -EINVAL; +} +EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_ops); + +static int vga_switcheroo_runtime_resume_hdmi_audio(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + int ret; + struct vga_switcheroo_client *client, *found = NULL; + + /* we need to check if we have to switch back on the video + device so the audio device can come back */ + list_for_each_entry(client, &vgasr_priv.clients, list) { + if (PCI_SLOT(client->pdev->devfn) == PCI_SLOT(pdev->devfn) && client_is_vga(client)) { + found = client; + ret = pm_runtime_get_sync(&client->pdev->dev); + if (ret) { + if (ret != 1) + return ret; + } + break; + } + } + ret = dev->bus->pm->runtime_resume(dev); + + /* put the reference for the gpu */ + if (found) { + pm_runtime_mark_last_busy(&found->pdev->dev); + pm_runtime_put_autosuspend(&found->pdev->dev); + } + return ret; +} + +int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain) +{ + /* copy over all the bus versions */ + if (dev->bus && dev->bus->pm) { + domain->ops = *dev->bus->pm; + domain->ops.runtime_resume = vga_switcheroo_runtime_resume_hdmi_audio; + + dev->pm_domain = domain; + return 0; + } + dev->pm_domain = NULL; + return -EINVAL; +} +EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_optimus_hdmi_audio); diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h index ddb419cf4530..502073a53dd3 100644 --- a/include/linux/vga_switcheroo.h +++ b/include/linux/vga_switcheroo.h @@ -45,7 +45,8 @@ struct vga_switcheroo_client_ops { #if defined(CONFIG_VGA_SWITCHEROO) void vga_switcheroo_unregister_client(struct pci_dev *dev); int vga_switcheroo_register_client(struct pci_dev *dev, - const struct vga_switcheroo_client_ops *ops); + const struct vga_switcheroo_client_ops *ops, + bool driver_power_control); int vga_switcheroo_register_audio_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, int id, bool active); @@ -60,11 +61,15 @@ int vga_switcheroo_process_delayed_switch(void); int vga_switcheroo_get_client_state(struct pci_dev *dev); +void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic); + +int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain); +int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain); #else static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {} static inline int vga_switcheroo_register_client(struct pci_dev *dev, - const struct vga_switcheroo_client_ops *ops) { return 0; } + const struct vga_switcheroo_client_ops *ops, bool driver_power_control) { return 0; } static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {} static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; } static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev, @@ -74,6 +79,10 @@ static inline void vga_switcheroo_unregister_handler(void) {} static inline int vga_switcheroo_process_delayed_switch(void) { return 0; } static inline int vga_switcheroo_get_client_state(struct pci_dev *dev) { return VGA_SWITCHEROO_ON; } +static inline void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic) {} + +static inline int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; } +static inline int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; } #endif #endif /* _LINUX_VGA_SWITCHEROO_H_ */ -- cgit v1.2.3-71-gd317 From 13bb9cc8726c716a7f271fc2c760ba15e1fdd38c Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 12 Sep 2012 15:55:05 +1000 Subject: drm: allow open of dynamic off devices. Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_fops.c | 2 +- include/drm/drmP.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 2d2401e9c5ae..136c949307ba 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -219,7 +219,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, return -EBUSY; /* No exclusive opens */ if (!drm_cpu_valid()) return -EINVAL; - if (dev->switch_power_state != DRM_SWITCH_POWER_ON) + if (dev->switch_power_state != DRM_SWITCH_POWER_ON && dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF) return -EINVAL; DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor_id); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 90833dccc919..0e3d51793b65 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1220,6 +1220,7 @@ struct drm_device { #define DRM_SWITCH_POWER_ON 0 #define DRM_SWITCH_POWER_OFF 1 #define DRM_SWITCH_POWER_CHANGING 2 +#define DRM_SWITCH_POWER_DYNAMIC_OFF 3 static __inline__ int drm_core_check_feature(struct drm_device *dev, int feature) -- cgit v1.2.3-71-gd317 From 7dc5dbc879bd0779924b5132a48b731a0bc04a1e Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 25 Mar 2013 20:07:01 -0700 Subject: sysfs: Restrict mounting sysfs Don't allow mounting sysfs unless the caller has CAP_SYS_ADMIN rights over the net namespace. The principle here is if you create or have capabilities over it you can mount it, otherwise you get to live with what other people have mounted. Instead of testing this with a straight forward ns_capable call, perform this check the long and torturous way with kobject helpers, this keeps direct knowledge of namespaces out of sysfs, and preserves the existing sysfs abstractions. Acked-by: Greg Kroah-Hartman Signed-off-by: "Eric W. Biederman" --- fs/sysfs/mount.c | 12 +++++++++--- include/linux/kobject_ns.h | 2 ++ lib/kobject.c | 15 +++++++++++++++ net/core/net-sysfs.c | 8 ++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 4a2da3a4b1b1..8c69ef49c7f3 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -112,9 +112,15 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, struct super_block *sb; int error; - if (!(flags & MS_KERNMOUNT) && !capable(CAP_SYS_ADMIN) && - !fs_fully_visible(fs_type)) - return ERR_PTR(-EPERM); + if (!(flags & MS_KERNMOUNT)) { + if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type)) + return ERR_PTR(-EPERM); + + for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) { + if (!kobj_ns_current_may_mount(type)) + return ERR_PTR(-EPERM); + } + } info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) diff --git a/include/linux/kobject_ns.h b/include/linux/kobject_ns.h index f66b065a8b5f..df32d2508290 100644 --- a/include/linux/kobject_ns.h +++ b/include/linux/kobject_ns.h @@ -39,6 +39,7 @@ enum kobj_ns_type { */ struct kobj_ns_type_operations { enum kobj_ns_type type; + bool (*current_may_mount)(void); void *(*grab_current_ns)(void); const void *(*netlink_ns)(struct sock *sk); const void *(*initial_ns)(void); @@ -50,6 +51,7 @@ int kobj_ns_type_registered(enum kobj_ns_type type); const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent); const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj); +bool kobj_ns_current_may_mount(enum kobj_ns_type type); void *kobj_ns_grab_current(enum kobj_ns_type type); const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk); const void *kobj_ns_initial(enum kobj_ns_type type); diff --git a/lib/kobject.c b/lib/kobject.c index 4a1f33d43548..3bbde222c90f 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -915,6 +915,21 @@ const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj) return kobj_child_ns_ops(kobj->parent); } +bool kobj_ns_current_may_mount(enum kobj_ns_type type) +{ + bool may_mount = false; + + if (type == KOBJ_NS_TYPE_NONE) + return true; + + spin_lock(&kobj_ns_type_lock); + if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && + kobj_ns_ops_tbl[type]) + may_mount = kobj_ns_ops_tbl[type]->current_may_mount(); + spin_unlock(&kobj_ns_type_lock); + + return may_mount; +} void *kobj_ns_grab_current(enum kobj_ns_type type) { diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 981fed397d1d..9bd9ae16adf5 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1157,6 +1157,13 @@ static void remove_queue_kobjects(struct net_device *net) #endif } +static bool net_current_may_mount(void) +{ + struct net *net = current->nsproxy->net_ns; + + return ns_capable(net->user_ns, CAP_SYS_ADMIN); +} + static void *net_grab_current_ns(void) { struct net *ns = current->nsproxy->net_ns; @@ -1179,6 +1186,7 @@ static const void *net_netlink_ns(struct sock *sk) struct kobj_ns_type_operations net_ns_type_operations = { .type = KOBJ_NS_TYPE_NET, + .current_may_mount = net_current_may_mount, .grab_current_ns = net_grab_current_ns, .netlink_ns = net_netlink_ns, .initial_ns = net_initial_ns, -- cgit v1.2.3-71-gd317 From 43622021d2e2b82ea03d883926605bdd0525e1d1 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 28 Aug 2013 22:29:55 +0200 Subject: HID: validate HID report id size The "Report ID" field of a HID report is used to build indexes of reports. The kernel's index of these is limited to 256 entries, so any malicious device that sets a Report ID greater than 255 will trigger memory corruption on the host: [ 1347.156239] BUG: unable to handle kernel paging request at ffff88094958a878 [ 1347.156261] IP: [] hid_register_report+0x2a/0x8b CVE-2013-2888 Signed-off-by: Kees Cook Cc: stable@kernel.org Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 10 +++++++--- include/linux/hid.h | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 36668d1aca8f..5ea7d51e45b9 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -63,6 +63,8 @@ struct hid_report *hid_register_report(struct hid_device *device, unsigned type, struct hid_report_enum *report_enum = device->report_enum + type; struct hid_report *report; + if (id >= HID_MAX_IDS) + return NULL; if (report_enum->report_id_hash[id]) return report_enum->report_id_hash[id]; @@ -404,8 +406,10 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_ID: parser->global.report_id = item_udata(item); - if (parser->global.report_id == 0) { - hid_err(parser->device, "report_id 0 is invalid\n"); + if (parser->global.report_id == 0 || + parser->global.report_id >= HID_MAX_IDS) { + hid_err(parser->device, "report_id %u is invalid\n", + parser->global.report_id); return -1; } return 0; @@ -575,7 +579,7 @@ static void hid_close_report(struct hid_device *device) for (i = 0; i < HID_REPORT_TYPES; i++) { struct hid_report_enum *report_enum = device->report_enum + i; - for (j = 0; j < 256; j++) { + for (j = 0; j < HID_MAX_IDS; j++) { struct hid_report *report = report_enum->report_id_hash[j]; if (report) hid_free_report(report); diff --git a/include/linux/hid.h b/include/linux/hid.h index 0c48991b0402..ff545cc33c3a 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -393,10 +393,12 @@ struct hid_report { struct hid_device *device; /* associated device */ }; +#define HID_MAX_IDS 256 + struct hid_report_enum { unsigned numbered; struct list_head report_list; - struct hid_report *report_id_hash[256]; + struct hid_report *report_id_hash[HID_MAX_IDS]; }; #define HID_REPORT_TYPES 3 -- cgit v1.2.3-71-gd317 From 0944fe3f4a323f436180d39402cae7f9c46ead17 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Tue, 23 Jul 2013 22:11:42 +0200 Subject: s390/mm: implement software referenced bits The last remaining use for the storage key of the s390 architecture is reference counting. The alternative is to make page table entries invalid while they are old. On access the fault handler marks the pte/pmd as young which makes the pte/pmd valid if the access rights allow read access. The pte/pmd invalidations required for software managed reference bits cost a bit of performance, on the other hand the RRBE/RRBM instructions to read and reset the referenced bits are quite expensive as well. Reviewed-by: Gerald Schaefer Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/page.h | 9 -- arch/s390/include/asm/pgtable.h | 345 +++++++++++++++++++++++----------------- arch/s390/mm/hugetlbpage.c | 58 ++++--- arch/s390/mm/pgtable.c | 18 +-- arch/s390/mm/vmem.c | 1 + include/asm-generic/pgtable.h | 4 - mm/rmap.c | 3 - 7 files changed, 244 insertions(+), 194 deletions(-) (limited to 'include') diff --git a/arch/s390/include/asm/page.h b/arch/s390/include/asm/page.h index 27f04801ec8b..1e51f2915b2e 100644 --- a/arch/s390/include/asm/page.h +++ b/arch/s390/include/asm/page.h @@ -140,15 +140,6 @@ static inline int page_reset_referenced(unsigned long addr) #define _PAGE_FP_BIT 0x08 /* HW fetch protection bit */ #define _PAGE_ACC_BITS 0xf0 /* HW access control bits */ -/* - * Test and clear referenced bit in storage key. - */ -#define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG -static inline int page_test_and_clear_young(unsigned long pfn) -{ - return page_reset_referenced(pfn << PAGE_SHIFT); -} - struct page; void arch_free_page(struct page *page, int order); void arch_alloc_page(struct page *page, int order); diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 1d144b6f3ba2..9f215b40109e 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -226,8 +226,9 @@ extern unsigned long MODULES_END; #define _PAGE_TYPE 0x002 /* SW pte type bit */ #define _PAGE_YOUNG 0x004 /* SW pte young bit */ #define _PAGE_DIRTY 0x008 /* SW pte dirty bit */ -#define _PAGE_WRITE 0x010 /* SW pte write bit */ -#define _PAGE_SPECIAL 0x020 /* SW associated with special page */ +#define _PAGE_READ 0x010 /* SW pte read bit */ +#define _PAGE_WRITE 0x020 /* SW pte write bit */ +#define _PAGE_SPECIAL 0x040 /* SW associated with special page */ #define __HAVE_ARCH_PTE_SPECIAL /* Set of bits not changed in pte_modify */ @@ -243,19 +244,25 @@ extern unsigned long MODULES_END; * The following table gives the different possible bit combinations for * the pte hardware and software bits in the last 12 bits of a pte: * - * 842100000000 - * 000084210000 - * 000000008421 - * .IR....wdytp - * empty .10....00000 - * swap .10....xxx10 - * file .11....xxxx0 - * prot-none, clean .11....00x01 - * prot-none, dirty .10....01x01 - * read-only, clean .01....00x01 - * read-only, dirty .01....01x01 - * read-write, clean .01....10x01 - * read-write, dirty .00....11x01 + * 842100000000 + * 000084210000 + * 000000008421 + * .IR...wrdytp + * empty .10...000000 + * swap .10...xxxx10 + * file .11...xxxxx0 + * prot-none, clean, old .11...000001 + * prot-none, clean, young .11...000101 + * prot-none, dirty, old .10...001001 + * prot-none, dirty, young .10...001101 + * read-only, clean, old .11...010001 + * read-only, clean, young .01...010101 + * read-only, dirty, old .11...011001 + * read-only, dirty, young .01...011101 + * read-write, clean, old .11...110001 + * read-write, clean, young .01...110101 + * read-write, dirty, old .10...111001 + * read-write, dirty, young .00...111101 * * pte_present is true for the bit pattern .xx...xxxxx1, (pte & 0x001) == 0x001 * pte_none is true for the bit pattern .10...xxxx00, (pte & 0x603) == 0x400 @@ -273,15 +280,26 @@ extern unsigned long MODULES_END; #define _ASCE_TABLE_LENGTH 0x7f /* 128 x 64 entries = 8k */ /* Bits in the segment table entry */ +#define _SEGMENT_ENTRY_BITS 0x7fffffffUL /* Valid segment table bits */ #define _SEGMENT_ENTRY_ORIGIN 0x7fffffc0UL /* page table origin */ #define _SEGMENT_ENTRY_PROTECT 0x200 /* page protection bit */ #define _SEGMENT_ENTRY_INVALID 0x20 /* invalid segment table entry */ #define _SEGMENT_ENTRY_COMMON 0x10 /* common segment bit */ #define _SEGMENT_ENTRY_PTL 0x0f /* page table length */ +#define _SEGMENT_ENTRY_NONE _SEGMENT_ENTRY_PROTECT #define _SEGMENT_ENTRY (_SEGMENT_ENTRY_PTL) #define _SEGMENT_ENTRY_EMPTY (_SEGMENT_ENTRY_INVALID) +/* + * Segment table entry encoding (I = invalid, R = read-only bit): + * ..R...I..... + * prot-none ..1...1..... + * read-only ..1...0..... + * read-write ..0...0..... + * empty ..0...1..... + */ + /* Page status table bits for virtualization */ #define PGSTE_ACC_BITS 0xf0000000UL #define PGSTE_FP_BIT 0x08000000UL @@ -290,9 +308,7 @@ extern unsigned long MODULES_END; #define PGSTE_HC_BIT 0x00200000UL #define PGSTE_GR_BIT 0x00040000UL #define PGSTE_GC_BIT 0x00020000UL -#define PGSTE_UR_BIT 0x00008000UL -#define PGSTE_UC_BIT 0x00004000UL /* user dirty (migration) */ -#define PGSTE_IN_BIT 0x00002000UL /* IPTE notify bit */ +#define PGSTE_IN_BIT 0x00008000UL /* IPTE notify bit */ #else /* CONFIG_64BIT */ @@ -331,6 +347,8 @@ extern unsigned long MODULES_END; #define _REGION3_ENTRY_CO 0x100 /* change-recording override */ /* Bits in the segment table entry */ +#define _SEGMENT_ENTRY_BITS 0xfffffffffffffe33UL +#define _SEGMENT_ENTRY_BITS_LARGE 0xfffffffffff1ff33UL #define _SEGMENT_ENTRY_ORIGIN_LARGE ~0xfffffUL /* large page address */ #define _SEGMENT_ENTRY_ORIGIN ~0x7ffUL/* segment table origin */ #define _SEGMENT_ENTRY_PROTECT 0x200 /* page protection bit */ @@ -342,6 +360,21 @@ extern unsigned long MODULES_END; #define _SEGMENT_ENTRY_LARGE 0x400 /* STE-format control, large page */ #define _SEGMENT_ENTRY_CO 0x100 /* change-recording override */ #define _SEGMENT_ENTRY_SPLIT 0x001 /* THP splitting bit */ +#define _SEGMENT_ENTRY_YOUNG 0x002 /* SW segment young bit */ +#define _SEGMENT_ENTRY_NONE _SEGMENT_ENTRY_YOUNG + +/* + * Segment table entry encoding (R = read-only, I = invalid, y = young bit): + * ..R...I...y. + * prot-none, old ..0...1...1. + * prot-none, young ..1...1...1. + * read-only, old ..1...1...0. + * read-only, young ..1...0...1. + * read-write, old ..0...1...0. + * read-write, young ..0...0...1. + * The segment table origin is used to distinguish empty (origin==0) from + * read-write, old segment table entries (origin!=0) + */ #define _SEGMENT_ENTRY_SPLIT_BIT 0 /* THP splitting bit number */ @@ -357,9 +390,7 @@ extern unsigned long MODULES_END; #define PGSTE_HC_BIT 0x0020000000000000UL #define PGSTE_GR_BIT 0x0004000000000000UL #define PGSTE_GC_BIT 0x0002000000000000UL -#define PGSTE_UR_BIT 0x0000800000000000UL -#define PGSTE_UC_BIT 0x0000400000000000UL /* user dirty (migration) */ -#define PGSTE_IN_BIT 0x0000200000000000UL /* IPTE notify bit */ +#define PGSTE_IN_BIT 0x0000800000000000UL /* IPTE notify bit */ #endif /* CONFIG_64BIT */ @@ -375,12 +406,17 @@ extern unsigned long MODULES_END; * Page protection definitions. */ #define PAGE_NONE __pgprot(_PAGE_PRESENT | _PAGE_INVALID) -#define PAGE_READ __pgprot(_PAGE_PRESENT | _PAGE_PROTECT) -#define PAGE_WRITE __pgprot(_PAGE_PRESENT | _PAGE_WRITE | _PAGE_PROTECT) - -#define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_WRITE | _PAGE_DIRTY) -#define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_WRITE | _PAGE_DIRTY) -#define PAGE_KERNEL_RO __pgprot(_PAGE_PRESENT | _PAGE_PROTECT) +#define PAGE_READ __pgprot(_PAGE_PRESENT | _PAGE_READ | \ + _PAGE_INVALID | _PAGE_PROTECT) +#define PAGE_WRITE __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \ + _PAGE_INVALID | _PAGE_PROTECT) + +#define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \ + _PAGE_YOUNG | _PAGE_DIRTY) +#define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \ + _PAGE_YOUNG | _PAGE_DIRTY) +#define PAGE_KERNEL_RO __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_YOUNG | \ + _PAGE_PROTECT) /* * On s390 the page table entry has an invalid bit and a read-only bit. @@ -410,9 +446,10 @@ extern unsigned long MODULES_END; * Segment entry (large page) protection definitions. */ #define SEGMENT_NONE __pgprot(_SEGMENT_ENTRY_INVALID | \ + _SEGMENT_ENTRY_NONE) +#define SEGMENT_READ __pgprot(_SEGMENT_ENTRY_INVALID | \ _SEGMENT_ENTRY_PROTECT) -#define SEGMENT_READ __pgprot(_SEGMENT_ENTRY_PROTECT) -#define SEGMENT_WRITE __pgprot(0) +#define SEGMENT_WRITE __pgprot(_SEGMENT_ENTRY_INVALID) static inline int mm_has_pgste(struct mm_struct *mm) { @@ -520,10 +557,19 @@ static inline int pmd_large(pmd_t pmd) #endif } +static inline int pmd_prot_none(pmd_t pmd) +{ + return (pmd_val(pmd) & _SEGMENT_ENTRY_INVALID) && + (pmd_val(pmd) & _SEGMENT_ENTRY_NONE); +} + static inline int pmd_bad(pmd_t pmd) { - unsigned long mask = ~_SEGMENT_ENTRY_ORIGIN & ~_SEGMENT_ENTRY_INVALID; - return (pmd_val(pmd) & mask) != _SEGMENT_ENTRY; +#ifdef CONFIG_64BIT + if (pmd_large(pmd)) + return (pmd_val(pmd) & ~_SEGMENT_ENTRY_BITS_LARGE) != 0; +#endif + return (pmd_val(pmd) & ~_SEGMENT_ENTRY_BITS) != 0; } #define __HAVE_ARCH_PMDP_SPLITTING_FLUSH @@ -542,12 +588,21 @@ extern int pmdp_clear_flush_young(struct vm_area_struct *vma, #define __HAVE_ARCH_PMD_WRITE static inline int pmd_write(pmd_t pmd) { + if (pmd_prot_none(pmd)) + return 0; return (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT) == 0; } static inline int pmd_young(pmd_t pmd) { - return 0; + int young = 0; +#ifdef CONFIG_64BIT + if (pmd_prot_none(pmd)) + young = (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT) != 0; + else + young = (pmd_val(pmd) & _SEGMENT_ENTRY_YOUNG) != 0; +#endif + return young; } static inline int pte_present(pte_t pte) @@ -632,33 +687,28 @@ static inline void pgste_set(pte_t *ptep, pgste_t pgste) static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste) { #ifdef CONFIG_PGSTE - unsigned long address, bits; - unsigned char skey; + unsigned long address, bits, skey; if (pte_val(*ptep) & _PAGE_INVALID) return pgste; address = pte_val(*ptep) & PAGE_MASK; - skey = page_get_storage_key(address); + skey = (unsigned long) page_get_storage_key(address); bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED); - /* Clear page changed & referenced bit in the storage key */ - if (bits & _PAGE_CHANGED) + if (!(pgste_val(pgste) & PGSTE_HC_BIT) && (bits & _PAGE_CHANGED)) { + /* Transfer dirty + referenced bit to host bits in pgste */ + pgste_val(pgste) |= bits << 52; page_set_storage_key(address, skey ^ bits, 0); - else if (bits) + } else if (!(pgste_val(pgste) & PGSTE_HR_BIT) && + (bits & _PAGE_REFERENCED)) { + /* Transfer referenced bit to host bit in pgste */ + pgste_val(pgste) |= PGSTE_HR_BIT; page_reset_referenced(address); + } /* Transfer page changed & referenced bit to guest bits in pgste */ pgste_val(pgste) |= bits << 48; /* GR bit & GC bit */ - /* Get host changed & referenced bits from pgste */ - bits |= (pgste_val(pgste) & (PGSTE_HR_BIT | PGSTE_HC_BIT)) >> 52; - /* Transfer page changed & referenced bit to kvm user bits */ - pgste_val(pgste) |= bits << 45; /* PGSTE_UR_BIT & PGSTE_UC_BIT */ - /* Clear relevant host bits in pgste. */ - pgste_val(pgste) &= ~(PGSTE_HR_BIT | PGSTE_HC_BIT); - pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT); /* Copy page access key and fetch protection bit to pgste */ - pgste_val(pgste) |= - (unsigned long) (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56; - /* Transfer referenced bit to pte */ - pte_val(*ptep) |= (bits & _PAGE_REFERENCED) << 1; + pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT); + pgste_val(pgste) |= (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56; #endif return pgste; @@ -667,24 +717,11 @@ static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste) static inline pgste_t pgste_update_young(pte_t *ptep, pgste_t pgste) { #ifdef CONFIG_PGSTE - int young; - if (pte_val(*ptep) & _PAGE_INVALID) return pgste; /* Get referenced bit from storage key */ - young = page_reset_referenced(pte_val(*ptep) & PAGE_MASK); - if (young) - pgste_val(pgste) |= PGSTE_GR_BIT; - /* Get host referenced bit from pgste */ - if (pgste_val(pgste) & PGSTE_HR_BIT) { - pgste_val(pgste) &= ~PGSTE_HR_BIT; - young = 1; - } - /* Transfer referenced bit to kvm user bits and pte */ - if (young) { - pgste_val(pgste) |= PGSTE_UR_BIT; - pte_val(*ptep) |= _PAGE_YOUNG; - } + if (page_reset_referenced(pte_val(*ptep) & PAGE_MASK)) + pgste_val(pgste) |= PGSTE_HR_BIT | PGSTE_GR_BIT; #endif return pgste; } @@ -839,11 +876,7 @@ static inline int pte_dirty(pte_t pte) static inline int pte_young(pte_t pte) { -#ifdef CONFIG_PGSTE - if (pte_val(pte) & _PAGE_YOUNG) - return 1; -#endif - return 0; + return (pte_val(pte) & _PAGE_YOUNG) != 0; } /* @@ -884,6 +917,16 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) { pte_val(pte) &= _PAGE_CHG_MASK; pte_val(pte) |= pgprot_val(newprot); + /* + * newprot for PAGE_NONE, PAGE_READ and PAGE_WRITE has the + * invalid bit set, clear it again for readable, young pages + */ + if ((pte_val(pte) & _PAGE_YOUNG) && (pte_val(pte) & _PAGE_READ)) + pte_val(pte) &= ~_PAGE_INVALID; + /* + * newprot for PAGE_READ and PAGE_WRITE has the page protection + * bit set, clear it again for writable, dirty pages + */ if ((pte_val(pte) & _PAGE_DIRTY) && (pte_val(pte) & _PAGE_WRITE)) pte_val(pte) &= ~_PAGE_PROTECT; return pte; @@ -921,14 +964,16 @@ static inline pte_t pte_mkdirty(pte_t pte) static inline pte_t pte_mkold(pte_t pte) { -#ifdef CONFIG_PGSTE pte_val(pte) &= ~_PAGE_YOUNG; -#endif + pte_val(pte) |= _PAGE_INVALID; return pte; } static inline pte_t pte_mkyoung(pte_t pte) { + pte_val(pte) |= _PAGE_YOUNG; + if (pte_val(pte) & _PAGE_READ) + pte_val(pte) &= ~_PAGE_INVALID; return pte; } @@ -958,8 +1003,8 @@ static inline int ptep_test_and_clear_user_dirty(struct mm_struct *mm, if (mm_has_pgste(mm)) { pgste = pgste_get_lock(ptep); pgste = pgste_update_all(ptep, pgste); - dirty = !!(pgste_val(pgste) & PGSTE_UC_BIT); - pgste_val(pgste) &= ~PGSTE_UC_BIT; + dirty = !!(pgste_val(pgste) & PGSTE_HC_BIT); + pgste_val(pgste) &= ~PGSTE_HC_BIT; pgste_set_unlock(ptep, pgste); return dirty; } @@ -978,42 +1023,13 @@ static inline int ptep_test_and_clear_user_young(struct mm_struct *mm, if (mm_has_pgste(mm)) { pgste = pgste_get_lock(ptep); pgste = pgste_update_young(ptep, pgste); - young = !!(pgste_val(pgste) & PGSTE_UR_BIT); - pgste_val(pgste) &= ~PGSTE_UR_BIT; + young = !!(pgste_val(pgste) & PGSTE_HR_BIT); + pgste_val(pgste) &= ~PGSTE_HR_BIT; pgste_set_unlock(ptep, pgste); } return young; } -#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG -static inline int ptep_test_and_clear_young(struct vm_area_struct *vma, - unsigned long addr, pte_t *ptep) -{ - pgste_t pgste; - pte_t pte; - - if (mm_has_pgste(vma->vm_mm)) { - pgste = pgste_get_lock(ptep); - pgste = pgste_update_young(ptep, pgste); - pte = *ptep; - *ptep = pte_mkold(pte); - pgste_set_unlock(ptep, pgste); - return pte_young(pte); - } - return 0; -} - -#define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH -static inline int ptep_clear_flush_young(struct vm_area_struct *vma, - unsigned long address, pte_t *ptep) -{ - /* No need to flush TLB - * On s390 reference bits are in storage key and never in TLB - * With virtualization we handle the reference bit, without we - * we can simply return */ - return ptep_test_and_clear_young(vma, address, ptep); -} - static inline void __ptep_ipte(unsigned long address, pte_t *ptep) { if (!(pte_val(*ptep) & _PAGE_INVALID)) { @@ -1042,6 +1058,40 @@ static inline void ptep_flush_lazy(struct mm_struct *mm, mm->context.flush_mm = 1; } +#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG +static inline int ptep_test_and_clear_young(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep) +{ + pgste_t pgste; + pte_t pte; + int young; + + if (mm_has_pgste(vma->vm_mm)) { + pgste = pgste_get_lock(ptep); + pgste = pgste_ipte_notify(vma->vm_mm, addr, ptep, pgste); + } + + pte = *ptep; + __ptep_ipte(addr, ptep); + young = pte_young(pte); + pte = pte_mkold(pte); + + if (mm_has_pgste(vma->vm_mm)) { + pgste_set_pte(ptep, pte); + pgste_set_unlock(ptep, pgste); + } else + *ptep = pte; + + return young; +} + +#define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH +static inline int ptep_clear_flush_young(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep) +{ + return ptep_test_and_clear_young(vma, address, ptep); +} + /* * This is hard to understand. ptep_get_and_clear and ptep_clear_flush * both clear the TLB for the unmapped pte. The reason is that @@ -1229,7 +1279,7 @@ static inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot) { pte_t __pte; pte_val(__pte) = physpage + pgprot_val(pgprot); - return __pte; + return pte_mkyoung(__pte); } static inline pte_t mk_pte(struct page *page, pgprot_t pgprot) @@ -1338,10 +1388,41 @@ static inline unsigned long massage_pgprot_pmd(pgprot_t pgprot) return pgprot_val(SEGMENT_WRITE); } +static inline pmd_t pmd_mkyoung(pmd_t pmd) +{ +#ifdef CONFIG_64BIT + if (pmd_prot_none(pmd)) { + pmd_val(pmd) |= _SEGMENT_ENTRY_PROTECT; + } else { + pmd_val(pmd) |= _SEGMENT_ENTRY_YOUNG; + pmd_val(pmd) &= ~_SEGMENT_ENTRY_INVALID; + } +#endif + return pmd; +} + +static inline pmd_t pmd_mkold(pmd_t pmd) +{ +#ifdef CONFIG_64BIT + if (pmd_prot_none(pmd)) { + pmd_val(pmd) &= ~_SEGMENT_ENTRY_PROTECT; + } else { + pmd_val(pmd) &= ~_SEGMENT_ENTRY_YOUNG; + pmd_val(pmd) |= _SEGMENT_ENTRY_INVALID; + } +#endif + return pmd; +} + static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) { + int young; + + young = pmd_young(pmd); pmd_val(pmd) &= _SEGMENT_CHG_MASK; pmd_val(pmd) |= massage_pgprot_pmd(newprot); + if (young) + pmd = pmd_mkyoung(pmd); return pmd; } @@ -1349,13 +1430,13 @@ static inline pmd_t mk_pmd_phys(unsigned long physpage, pgprot_t pgprot) { pmd_t __pmd; pmd_val(__pmd) = physpage + massage_pgprot_pmd(pgprot); - return __pmd; + return pmd_mkyoung(__pmd); } static inline pmd_t pmd_mkwrite(pmd_t pmd) { - /* Do not clobber PROT_NONE pages! */ - if (!(pmd_val(pmd) & _SEGMENT_ENTRY_INVALID)) + /* Do not clobber PROT_NONE segments! */ + if (!pmd_prot_none(pmd)) pmd_val(pmd) &= ~_SEGMENT_ENTRY_PROTECT; return pmd; } @@ -1391,7 +1472,9 @@ static inline pmd_t pmd_mkhuge(pmd_t pmd) static inline pmd_t pmd_wrprotect(pmd_t pmd) { - pmd_val(pmd) |= _SEGMENT_ENTRY_PROTECT; + /* Do not clobber PROT_NONE segments! */ + if (!pmd_prot_none(pmd)) + pmd_val(pmd) |= _SEGMENT_ENTRY_PROTECT; return pmd; } @@ -1401,50 +1484,16 @@ static inline pmd_t pmd_mkdirty(pmd_t pmd) return pmd; } -static inline pmd_t pmd_mkold(pmd_t pmd) -{ - /* No referenced bit in the segment table entry. */ - return pmd; -} - -static inline pmd_t pmd_mkyoung(pmd_t pmd) -{ - /* No referenced bit in the segment table entry. */ - return pmd; -} - #define __HAVE_ARCH_PMDP_TEST_AND_CLEAR_YOUNG static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma, unsigned long address, pmd_t *pmdp) { - unsigned long pmd_addr = pmd_val(*pmdp) & HPAGE_MASK; - long tmp, rc; - int counter; + pmd_t pmd; - rc = 0; - if (MACHINE_HAS_RRBM) { - counter = PTRS_PER_PTE >> 6; - asm volatile( - "0: .insn rre,0xb9ae0000,%0,%3\n" /* rrbm */ - " ogr %1,%0\n" - " la %3,0(%4,%3)\n" - " brct %2,0b\n" - : "=&d" (tmp), "+&d" (rc), "+d" (counter), - "+a" (pmd_addr) - : "a" (64 * 4096UL) : "cc"); - rc = !!rc; - } else { - counter = PTRS_PER_PTE; - asm volatile( - "0: rrbe 0,%2\n" - " la %2,0(%3,%2)\n" - " brc 12,1f\n" - " lhi %0,1\n" - "1: brct %1,0b\n" - : "+d" (rc), "+d" (counter), "+a" (pmd_addr) - : "a" (4096UL) : "cc"); - } - return rc; + pmd = *pmdp; + __pmd_idte(address, pmdp); + *pmdp = pmd_mkold(pmd); + return pmd_young(pmd); } #define __HAVE_ARCH_PMDP_GET_AND_CLEAR diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index b0bd0ae17796..248445f92604 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c @@ -10,19 +10,25 @@ static inline pmd_t __pte_to_pmd(pte_t pte) { - int none, prot; + int none, young, prot; pmd_t pmd; /* - * Convert encoding pte bits pmd bits - * .IR.....wdtp ..R...I..... - * empty .10.....0000 -> ..0...1..... - * prot-none, clean .11.....0001 -> ..1...1..... - * prot-none, dirty .10.....0101 -> ..1...1..... - * read-only, clean .01.....0001 -> ..1...0..... - * read-only, dirty .01.....0101 -> ..1...0..... - * read-write, clean .01.....1001 -> ..0...0..... - * read-write, dirty .00.....1101 -> ..0...0..... + * Convert encoding pte bits pmd bits + * .IR...wrdytp ..R...I...y. + * empty .10...000000 -> ..0...1...0. + * prot-none, clean, old .11...000001 -> ..0...1...1. + * prot-none, clean, young .11...000101 -> ..1...1...1. + * prot-none, dirty, old .10...001001 -> ..0...1...1. + * prot-none, dirty, young .10...001101 -> ..1...1...1. + * read-only, clean, old .11...010001 -> ..1...1...0. + * read-only, clean, young .01...010101 -> ..1...0...1. + * read-only, dirty, old .11...011001 -> ..1...1...0. + * read-only, dirty, young .01...011101 -> ..1...0...1. + * read-write, clean, old .11...110001 -> ..0...1...0. + * read-write, clean, young .01...110101 -> ..0...0...1. + * read-write, dirty, old .10...111001 -> ..0...1...0. + * read-write, dirty, young .00...111101 -> ..0...0...1. * Huge ptes are dirty by definition, a clean pte is made dirty * by the conversion. */ @@ -31,9 +37,14 @@ static inline pmd_t __pte_to_pmd(pte_t pte) if (pte_val(pte) & _PAGE_INVALID) pmd_val(pmd) |= _SEGMENT_ENTRY_INVALID; none = (pte_val(pte) & _PAGE_PRESENT) && - (pte_val(pte) & _PAGE_INVALID); - prot = (pte_val(pte) & _PAGE_PROTECT); - if (prot || none) + !(pte_val(pte) & _PAGE_READ) && + !(pte_val(pte) & _PAGE_WRITE); + prot = (pte_val(pte) & _PAGE_PROTECT) && + !(pte_val(pte) & _PAGE_WRITE); + young = pte_val(pte) & _PAGE_YOUNG; + if (none || young) + pmd_val(pmd) |= _SEGMENT_ENTRY_YOUNG; + if (prot || (none && young)) pmd_val(pmd) |= _SEGMENT_ENTRY_PROTECT; } else pmd_val(pmd) = _SEGMENT_ENTRY_INVALID; @@ -46,11 +57,14 @@ static inline pte_t __pmd_to_pte(pmd_t pmd) /* * Convert encoding pmd bits pte bits - * ..R...I..... .IR.....wdtp - * empty ..0...1..... -> .10.....0000 - * prot-none, young ..1...1..... -> .10.....0101 - * read-only, young ..1...0..... -> .01.....0101 - * read-write, young ..0...0..... -> .00.....1101 + * ..R...I...y. .IR...wrdytp + * empty ..0...1...0. -> .10...000000 + * prot-none, old ..0...1...1. -> .10...001001 + * prot-none, young ..1...1...1. -> .10...001101 + * read-only, old ..1...1...0. -> .11...011001 + * read-only, young ..1...0...1. -> .01...011101 + * read-write, old ..0...1...0. -> .10...111001 + * read-write, young ..0...0...1. -> .00...111101 * Huge ptes are dirty by definition */ if (pmd_present(pmd)) { @@ -58,11 +72,17 @@ static inline pte_t __pmd_to_pte(pmd_t pmd) (pmd_val(pmd) & PAGE_MASK); if (pmd_val(pmd) & _SEGMENT_ENTRY_INVALID) pte_val(pte) |= _PAGE_INVALID; - else { + if (pmd_prot_none(pmd)) { + if (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT) + pte_val(pte) |= _PAGE_YOUNG; + } else { + pte_val(pte) |= _PAGE_READ; if (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT) pte_val(pte) |= _PAGE_PROTECT; else pte_val(pte) |= _PAGE_WRITE; + if (pmd_val(pmd) & _SEGMENT_ENTRY_YOUNG) + pte_val(pte) |= _PAGE_YOUNG; } } else pte_val(pte) = _PAGE_INVALID; diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index befaea7003f7..6d16132d0850 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -754,7 +754,8 @@ static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm, atomic_set(&page->_mapcount, 3); table = (unsigned long *) page_to_phys(page); clear_table(table, _PAGE_INVALID, PAGE_SIZE/2); - clear_table(table + PTRS_PER_PTE, 0, PAGE_SIZE/2); + clear_table(table + PTRS_PER_PTE, PGSTE_HR_BIT | PGSTE_HC_BIT, + PAGE_SIZE/2); return table; } @@ -792,26 +793,21 @@ int set_guest_storage_key(struct mm_struct *mm, unsigned long addr, pgste_val(new) |= (key & (_PAGE_CHANGED | _PAGE_REFERENCED)) << 48; pgste_val(new) |= (key & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56; if (!(pte_val(*ptep) & _PAGE_INVALID)) { - unsigned long address, bits; - unsigned char skey; + unsigned long address, bits, skey; address = pte_val(*ptep) & PAGE_MASK; - skey = page_get_storage_key(address); + skey = (unsigned long) page_get_storage_key(address); bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED); + skey = key & (_PAGE_ACC_BITS | _PAGE_FP_BIT); /* Set storage key ACC and FP */ - page_set_storage_key(address, - (key & (_PAGE_ACC_BITS | _PAGE_FP_BIT)), - !nq); - + page_set_storage_key(address, skey, !nq); /* Merge host changed & referenced into pgste */ pgste_val(new) |= bits << 52; - /* Transfer skey changed & referenced bit to kvm user bits */ - pgste_val(new) |= bits << 45; /* PGSTE_UR_BIT & PGSTE_UC_BIT */ } /* changing the guest storage key is considered a change of the page */ if ((pgste_val(new) ^ pgste_val(old)) & (PGSTE_ACC_BITS | PGSTE_FP_BIT | PGSTE_GR_BIT | PGSTE_GC_BIT)) - pgste_val(new) |= PGSTE_UC_BIT; + pgste_val(new) |= PGSTE_HC_BIT; pgste_set_unlock(ptep, new); pte_unmap_unlock(*ptep, ptl); diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index e1299d40818d..bcfb70b60be6 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -118,6 +118,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro) !(address & ~PMD_MASK) && (address + PMD_SIZE <= end)) { pmd_val(*pm_dir) = __pa(address) | _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE | + _SEGMENT_ENTRY_YOUNG | (ro ? _SEGMENT_ENTRY_PROTECT : 0); address += PMD_SIZE; continue; diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 0807ddf97b05..f330d28e4d0e 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -208,10 +208,6 @@ static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b) #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ #endif -#ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG -#define page_test_and_clear_young(pfn) (0) -#endif - #ifndef __HAVE_ARCH_PGD_OFFSET_GATE #define pgd_offset_gate(mm, addr) pgd_offset(mm, addr) #endif diff --git a/mm/rmap.c b/mm/rmap.c index b2e29acd7e3d..07748e68b729 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -873,9 +873,6 @@ int page_referenced(struct page *page, vm_flags); if (we_locked) unlock_page(page); - - if (page_test_and_clear_young(page_to_pfn(page))) - referenced++; } out: return referenced; -- cgit v1.2.3-71-gd317 From 9453e79335da4a49031341e58ce6faf343d9bb6d Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 24 Jul 2013 12:49:31 +0300 Subject: OMAPDSS: remove omap_dss_device->channel field The 'channel' field in struct omap_dss_device is no longer used, and can be removed. Signed-off-by: Tomi Valkeinen Reviewed-by: Archit Taneja --- include/video/omapdss.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/video/omapdss.h b/include/video/omapdss.h index b39463553845..3b68dc2c6410 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h @@ -765,9 +765,6 @@ struct omap_dss_device { enum omap_display_type type; enum omap_display_type output_type; - /* obsolete, to be removed */ - enum omap_channel channel; - union { struct { u8 data_lines; -- cgit v1.2.3-71-gd317 From 05cefbe2f22e551594c5ec618102a35317bd050d Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 15 May 2013 10:36:01 +0300 Subject: OMAPDSS: remove all old panel drivers The board files now use the new panel drivers, making the old panel drivers obsolete. Remove the old panel drivers, Kconfig and Makefile entries, and the panels' platform data structs. Signed-off-by: Tomi Valkeinen Reviewed-by: Archit Taneja --- drivers/video/omap2/Kconfig | 1 - drivers/video/omap2/Makefile | 1 - drivers/video/omap2/displays/Kconfig | 75 - drivers/video/omap2/displays/Makefile | 11 - drivers/video/omap2/displays/panel-acx565akm.c | 798 ---------- drivers/video/omap2/displays/panel-generic-dpi.c | 744 ---------- .../omap2/displays/panel-lgphilips-lb035q02.c | 262 ---- drivers/video/omap2/displays/panel-n8x0.c | 616 -------- .../omap2/displays/panel-nec-nl8048hl11-01b.c | 290 ---- drivers/video/omap2/displays/panel-picodlp.c | 559 ------- drivers/video/omap2/displays/panel-picodlp.h | 288 ---- .../video/omap2/displays/panel-sharp-ls037v7dw01.c | 198 --- drivers/video/omap2/displays/panel-taal.c | 1551 -------------------- drivers/video/omap2/displays/panel-tfp410.c | 353 ----- .../video/omap2/displays/panel-tpo-td043mtea1.c | 596 -------- include/video/omap-panel-data.h | 118 -- 16 files changed, 6461 deletions(-) delete mode 100644 drivers/video/omap2/displays/Kconfig delete mode 100644 drivers/video/omap2/displays/Makefile delete mode 100644 drivers/video/omap2/displays/panel-acx565akm.c delete mode 100644 drivers/video/omap2/displays/panel-generic-dpi.c delete mode 100644 drivers/video/omap2/displays/panel-lgphilips-lb035q02.c delete mode 100644 drivers/video/omap2/displays/panel-n8x0.c delete mode 100644 drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c delete mode 100644 drivers/video/omap2/displays/panel-picodlp.c delete mode 100644 drivers/video/omap2/displays/panel-picodlp.h delete mode 100644 drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c delete mode 100644 drivers/video/omap2/displays/panel-taal.c delete mode 100644 drivers/video/omap2/displays/panel-tfp410.c delete mode 100644 drivers/video/omap2/displays/panel-tpo-td043mtea1.c (limited to 'include') diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig index 56cad0f5386c..63b23f87081d 100644 --- a/drivers/video/omap2/Kconfig +++ b/drivers/video/omap2/Kconfig @@ -5,7 +5,6 @@ if ARCH_OMAP2PLUS source "drivers/video/omap2/dss/Kconfig" source "drivers/video/omap2/omapfb/Kconfig" -source "drivers/video/omap2/displays/Kconfig" source "drivers/video/omap2/displays-new/Kconfig" endif diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile index 86873c2fbb27..bf8127df8c71 100644 --- a/drivers/video/omap2/Makefile +++ b/drivers/video/omap2/Makefile @@ -1,6 +1,5 @@ obj-$(CONFIG_OMAP2_VRFB) += vrfb.o obj-$(CONFIG_OMAP2_DSS) += dss/ -obj-y += displays/ obj-y += displays-new/ obj-$(CONFIG_FB_OMAP2) += omapfb/ diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig deleted file mode 100644 index e80ac1c79561..000000000000 --- a/drivers/video/omap2/displays/Kconfig +++ /dev/null @@ -1,75 +0,0 @@ -menu "OMAP2/3 Display Device Drivers (old device model)" - depends on OMAP2_DSS - -config PANEL_GENERIC_DPI - tristate "Generic DPI Panel" - depends on OMAP2_DSS_DPI - help - Generic DPI panel driver. - Supports DVI output for Beagle and OMAP3 SDP. - Supports LCD Panel used in TI SDP3430 and EVM boards, - OMAP3517 EVM boards and CM-T35. - -config PANEL_TFP410 - tristate "TFP410 DPI-to-DVI chip" - depends on OMAP2_DSS_DPI && I2C - help - Driver for TFP410 DPI-to-DVI chip. The driver uses i2c to read EDID - information from the monitor. - -config PANEL_LGPHILIPS_LB035Q02 - tristate "LG.Philips LB035Q02 LCD Panel" - depends on OMAP2_DSS_DPI && SPI - help - LCD Panel used on the Gumstix Overo Palo35 - -config PANEL_SHARP_LS037V7DW01 - tristate "Sharp LS037V7DW01 LCD Panel" - depends on OMAP2_DSS_DPI - depends on BACKLIGHT_CLASS_DEVICE - help - LCD Panel used in TI's SDP3430 and EVM boards - -config PANEL_NEC_NL8048HL11_01B - tristate "NEC NL8048HL11-01B Panel" - depends on OMAP2_DSS_DPI - depends on SPI - depends on BACKLIGHT_CLASS_DEVICE - help - This NEC NL8048HL11-01B panel is TFT LCD - used in the Zoom2/3/3630 sdp boards. - -config PANEL_PICODLP - tristate "TI PICO DLP mini-projector" - depends on OMAP2_DSS_DPI && I2C - help - A mini-projector used in TI's SDP4430 and EVM boards - For more info please visit http://www.dlp.com/projector/ - -config PANEL_TAAL - tristate "Taal DSI Panel" - depends on OMAP2_DSS_DSI - depends on BACKLIGHT_CLASS_DEVICE - help - Taal DSI command mode panel from TPO. - -config PANEL_TPO_TD043MTEA1 - tristate "TPO TD043MTEA1 LCD Panel" - depends on OMAP2_DSS_DPI && SPI - help - LCD Panel used in OMAP3 Pandora - -config PANEL_ACX565AKM - tristate "ACX565AKM Panel" - depends on OMAP2_DSS_SDI && SPI - depends on BACKLIGHT_CLASS_DEVICE - help - This is the LCD panel used on Nokia N900 - -config PANEL_N8X0 - tristate "N8X0 Panel" - depends on OMAP2_DSS_RFBI && SPI - depends on BACKLIGHT_CLASS_DEVICE - help - This is the LCD panel used on Nokia N8x0 -endmenu diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile deleted file mode 100644 index 58a5176b07b0..000000000000 --- a/drivers/video/omap2/displays/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o -obj-$(CONFIG_PANEL_TFP410) += panel-tfp410.o -obj-$(CONFIG_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o -obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o -obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o - -obj-$(CONFIG_PANEL_TAAL) += panel-taal.o -obj-$(CONFIG_PANEL_PICODLP) += panel-picodlp.o -obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o -obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o -obj-$(CONFIG_PANEL_N8X0) += panel-n8x0.o diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c deleted file mode 100644 index 3fd100fc853e..000000000000 --- a/drivers/video/omap2/displays/panel-acx565akm.c +++ /dev/null @@ -1,798 +0,0 @@ -/* - * Support for ACX565AKM LCD Panel used on Nokia N900 - * - * Copyright (C) 2010 Nokia Corporation - * - * Original Driver Author: Imre Deak - * Based on panel-generic.c by Tomi Valkeinen - * Adapted to new DSS2 framework: Roger Quadros - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include